# -*- encoding: utf-8 -*-
module Jekyll
  module Commands
    class Serve < Command

      class << self

        def init_with_program(prog)
          prog.command(:serve) do |c|
            c.syntax 'serve [options]'
            c.description 'Serve your site locally'
            c.alias :server
            c.alias :s

            add_build_options(c)

            c.option 'detach', '-B', '--detach', 'Run the server in the background (detach)'
            c.option 'port', '-P', '--port [PORT]', 'Port to listen on'
            c.option 'host', '-H', '--host [HOST]', 'Host to bind to'
            c.option 'baseurl', '-b', '--baseurl [URL]', 'Base URL'
            c.option 'skip_initial_build', '--skip-initial-build', 'Skips the initial site build which occurs before the server is started.'

            c.action do |args, options|
              options["serving"] = true
              options["watch"] = true unless options.key?("watch")
              Jekyll::Commands::Build.process(options)
              Jekyll::Commands::Serve.process(options)
            end
          end
        end

        # Boot up a WEBrick server which points to the compiled site's root.
        def process(options)
          options = configuration_from_options(options)
          destination = options['destination']
          setup(destination)

          s = WEBrick::HTTPServer.new(webrick_options(options))
          s.unmount("")

          s.mount(
            options['baseurl'],
            WEBrick::HTTPServlet::FileHandler,
            destination,
            file_handler_options
          )

          Jekyll.logger.info "Server address:", server_address(s, options)

          if options['detach'] # detach the server
            pid = Process.fork { s.start }
            Process.detach(pid)
            Jekyll.logger.info "Server detached with pid '#{pid}'.", "Run `kill -9 #{pid}' to stop the server."
          else # create a new server thread, then join it with current terminal
            t = Thread.new { s.start }
            trap("INT") { s.shutdown }
            t.join
          end
        end

        def setup(destination)
          require 'webrick'

          FileUtils.mkdir_p(destination)

          # monkey patch WEBrick using custom 404 page (/404.html)
          if File.exist?(File.join(destination, '404.html'))
            WEBrick::HTTPResponse.class_eval do
              def create_error_page
                @header['content-type'] = "text/html; charset=UTF-8"
                @body = IO.read(File.join(@config[:DocumentRoot], '404.html'))
              end
            end
          end
        end

        def webrick_options(config)
          opts = {
            :BindAddress        => config['host'],
            :DirectoryIndex     => %w(index.html index.htm index.cgi index.rhtml index.xml),
            :DocumentRoot       => config['destination'],
            :DoNotReverseLookup => true,
            :MimeTypes          => mime_types,
            :Port               => config['port'],
            :StartCallback      => start_callback(config['detach'])
          }

          if config['verbose']
            opts.merge!({
              :Logger => WEBrick::Log.new($stdout, WEBrick::Log::DEBUG)
            })
          else
            opts.merge!({
              :AccessLog => [],
              :Logger => WEBrick::Log.new([], WEBrick::Log::WARN)
            })
          end

          opts
        end

        def start_callback(detached)
          unless detached
            Proc.new { Jekyll.logger.info "Server running...", "press ctrl-c to stop." }
          end
        end

        def mime_types
          mime_types_file = File.expand_path('../mime.types', File.dirname(__FILE__))
          WEBrick::HTTPUtils::load_mime_types(mime_types_file)
        end

        def server_address(server, options)
          baseurl = "#{options['baseurl']}/" if options['baseurl']
          [
            "http://",
            server.config[:BindAddress],
            ":",
            server.config[:Port],
            baseurl || ""
          ].map(&:to_s).join("")
        end

        # recreate NondisclosureName under utf-8 circumstance
        def file_handler_options
          WEBrick::Config::FileHandler.merge({
            :FancyIndexing     => true,
            :NondisclosureName => ['.ht*','~*']
          })
        end

      end

    end
  end
end
