require 'rubygems'
require 'factor'
require 'mustache'
#require 'facter'


module Factor
  module Runtime
    class Engine
      attr_accessor :channel_modules, :workflows, :message_bus
    
      # Engine needs modules that contain the code, workflows to run, and message bus for communication
      def initialize(username,token)
        @channel_modules=Hash.new
        @workflows = Hash.new
        @message_bus = MessageBus.new(username,token)
        @credentials = Hash.new
        @tags = Hash.new
      end
    
      def tag(key,value)
        @tags[key]=value
      end
    
      # load the channel by referencing the .rb file
      # the filename is lowercase with "_" for spaces
      # and the module inside must be camal cased to match
      # once loaded it is in the channel_modules Hash
      def load_channel filename
        filename = File.absolute_path(File.expand_path(filename.path)) if filename.is_a?(File) # just in case someone passes in a File not a String (i.e. me)
        require filename
        channel_module_name = File.basename(filename).gsub('.rb','').split('_').map{|ea| ea.capitalize}.join('')
        channel_module= self.class.const_get(channel_module_name)
        @channel_modules[channel_module_name]=channel_module  
      end
    
      def load_credentials credentials
        @credentials["credentials"] = credentials
      end
    
      # adds the workflow to the workflows list
      # the object must be a Workflow type
      def load_workflow workflow
        @workflows[workflow.name] = workflow
      end
    
      def logs routing_key="#", &code 
        @message_bus.start do
          @message_bus.listen routing_key do |message|
            code.call message
          end
        end
      end
    
      def launch workflow, params
        instance_id=SecureRandom.hex
        
        @message_bus.start do
          message = Message.new
          message.position << "start"
          message.workflow=workflow
          message.add_values params
          message.workflow_instance_id= instance_id
          @message_bus.send_and_close message
        end
        instance_id
      end
    
      # start your engines. vroom vrooooom!
      def start
        begin
          @message_bus.start do
            @message_bus.listen do |message|
              if @workflows.include? message.workflow
                workflow = @workflows[message.workflow]
                activity = workflow.get_activity(message.position)
                if !activity.nil?
                  action = activity["action"]
                  channel = activity["channel"]
                  target = activity["target"]
                  params_template = activity["params"]
              
                  if match(target)              
                    values = message.body.merge(@credentials)

                    # this maps the input values passed in with the templated defined in the workflow
                    params = render_template(params_template,values)
                    event = call_channel_method(channel,action,params)
                    response_message = message.respond(event.params,event.class.name.split("::").last)
              
                    @message_bus.send response_message
                  end
                end
              else
                # workflow doesn't exist
              end
            end
          end
        rescue SystemExit, Interrupt
          "done"
        rescue Exception => ex
          ex
        end
      end

      def call_channel_method(channel_name,class_name,params)
        channel_module = @channel_modules[channel_name]
        command = channel_module.const_get(class_name)
        puts "command: #{command.inspect}"
        command.new.do_work(params)
      end
      
      private 
      
      def render_template(source,values)
        #params = Hash.new
        
        if source.is_a?(Hash)
          params = Hash.new
          source.each do |key,template|
            params[key]=render_template(template,values)
          end
        else
          params = Mustache.render(source,values)
        end
        
        # activity_params.each do |key,template|
        #   params[key]=Mustache.render(template,values)
        # end
        params
      end
      
      def match(target)
        return true if target==nil || target==""
        facts={}
        #Facter.each {|k,v| facts[k]=v}
        @tags.each {|k,v| facts[k]=v}
        key,value=target.split("=")
        facts[key]==value
      end
      
      
    end
  end
end