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
        file=File.new filename
        require file
        channel_module_name = File.basename(file).gsub('.rb','').split('_').map{|ea| ea.capitalize}.join('')
        channel_module= self.class.const_get(channel_module_name)
        @channel_modules[channel_module.definition["module"]]=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
        @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"]
                method = activity["method"]
                target = activity["target"]
              
                
                if match(target)
              
                  values = message.body.merge(@credentials)
                  # this maps the input values passed in with the templated defined in the workflow
                  params = Hash.new
                  activity["params"].each do |key,template|
                    params[key]=Mustache.render(template,values)
                  end
                  event = call_channel_method(channel,method,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
      end

      def call_channel_method(channel_name,class_name,params)
        channel_module = @channel_modules[channel_name]
        command = channel_module.const_get(class_name)
        command.new.do_work(params)
      end
      
      private 
      
      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