require 'webrick'
require 'json'
require 'net/http'

require 'rhcp/broker'
require 'rhcp/request'
require 'rhcp/rhcp_exception'

include WEBrick

module RHCP
  
  # TODO add some logging
  
  # The +HttpExporter+ connects to the RHCP::Broker and exports all commands
  # registered there via http, i.e. it servers requests to the URLs
  # 
  #   /rhcp/get_commands
  #   /rhcp/get_lookup_values
  #   /rhcp/execute
  #
  # All data is transferred in JSON format.
  class HttpExporter
    
    DEFAULT_PORT = 42000
    DEFAULT_PREFIX = "rhcp"
    
    # by default, a new HttpExporter will create it's own +HTTPServer+
    # on port 42000 - if you want to change this port, you can pass the port
    # to use as +port+ option. If you want to use an existing +HTTPServer+
    # instance, you can pass it as +server+ option instead.
    #
    # Also, you can specify the prefix for the rhcp URLs using the +prefix+
    # option - otherwise, all
    # rhcp actions will be exported at 
    #   /rhcp/get_commands
    #   /rhcp/get_lookup_values
    #   /rhcp/execute
    # If you change this, please be aware that all clients that want to connect
    # to this server need to be configured accordingly.
    def initialize(broker, options = Hash.new())
      
      @logger = RHCP::ModuleHelper::instance.logger
      @broker = broker

      # build your own server or use the one passed in as param
      port = options.has_key?(:port) ? options[:port] : DEFAULT_PORT
      
      if options.has_key?(:server)
        @server = options[:server]
        @logger.debug("using existing server #{@server}")
      else        
        @logger.debug("opening own server on port #{port}")
        @server = HTTPServer.new( :Port => port )
      end
      
      @url_prefix = options.has_key?(:prefix) ? options[:prefix] : DEFAULT_PREFIX
      @server.mount_proc("/#{@url_prefix}/") {|req, res|
        res.body = "<HTML>hello (again)</HTML>"
        res['Content-Type'] = "text/html"
      }
      # TODO this path should probably be quite relative
      @server.mount "/#{@url_prefix}/info", HTTPServlet::FileHandler, "docroot"
      @server.mount "/#{@url_prefix}/info2", HTTPServlet::FileHandler, "qooxdoo_rhcp/build"
      @server.mount "/#{@url_prefix}/get_commands", GetCommandsServlet, @broker
      @server.mount "/#{@url_prefix}/get_lookup_values", GetLookupValuesServlet, @broker
      @server.mount "/#{@url_prefix}/execute", ExecuteServlet, @broker
      @logger.info("http exporter has been initialized - once started, it will listen on port '#{port}' for URLs starting with prefix '#{@url_prefix}'")
    end
   
    class BaseServlet < HTTPServlet::AbstractServlet
      
      def initialize(server, broker)
        super(server)
        @broker = broker
      end
      
      def do_GET(req, res)
        res['Content-Type'] = "application/json"        
        begin
          #@logger.info("executing #{req}")
          do_it(req, res)
          #@logger.info("finished executing #{req}")
        rescue RHCP::RhcpException => ex
          # TODO define error format (should we send back a JSON-ified response object here?)           
          @logger.error("got an exception while executing request . #{ex}")
          res.status = 500
          res.body = [ ex.to_s ].to_json()
          #puts ex
          #puts ex.backtrace.join("\n")
        end
      end
      
      # TODO is this a good idea?
      def do_POST(req, res)
        do_GET(req, res)
      end
      
    end

    class GetCommandsServlet < BaseServlet      
      
      def do_it(req, res)
        commands = @broker.get_command_list
        puts "about to send back commands : #{commands.values.to_json()}"
        res.body = commands.values.to_json()
      end
    end

    class GetLookupValuesServlet < BaseServlet
      
      def do_it(req, res)
        partial_value = req.query.has_key?('partial') ? req.query['partial'] : ''
        command = @broker.get_command(req.query['command'])
        param = command.get_param(req.query['param'])
        lookup_values = param.get_lookup_values(partial_value)
        res.body = lookup_values.to_json()
      end
      
    end

    class ExecuteServlet < BaseServlet
      
      def do_it(req, res)
        @logger.info("got an execute request : #{req}")
        # TODO filter "nocache" parameters from query string
        request = RHCP::Request.reconstruct_from_json(@broker, req.body)

        response = request.execute()
        
        @logger.debug("sending back response : #{response.to_json()}")
        res.body = response.to_json()
      end
      
    end
    
    def run
      @server.start
    end
    
    def start
      puts "about to start server..."
      @thread = Thread.new {     
        puts "in thread; starting server..."        
        @server.start
        puts "in thread: continuing..."
      }      
    end
    
    def stop
      puts "about to stop server"
      @server.stop
      puts "thread stopped"
      # TODO wait until after the server has ended - when this method exits, the server is still shutting down
    end     
      
  end
  
end