module God
  
  class Watch < Base
    # config
    attr_accessor :name, :start, :stop, :restart, :interval, :grace
    
    # api
    attr_accessor :behaviors, :conditions
    
    # internal
    attr_accessor :mutex
    
    # 
    def initialize(meddle)
      @meddle = meddle
      
      # no grace period by default
      self.grace = 0
      
      # keep track of which action each condition belongs to
      @action = nil
      
      self.behaviors = []
      
      # the list of conditions for each action
      self.conditions = {:start => [],
                         :restart => []}
                         
      # mutex
      self.mutex = Mutex.new
    end
    
    def behavior(kind)
      # create the behavior
      begin
        b = Behavior.generate(kind)
      rescue NoSuchBehaviorError => e
        abort e.message
      end
      
      # send to block so config can set attributes
      yield(b) if block_given?
      
      # abort if the Behavior is invalid, the Behavior will have printed
      # out its own error messages by now
      abort unless b.valid?
      
      self.behaviors << b
    end
    
    def start_if
      @action = :start
      yield(self)
      @action = nil
    end
    
    def restart_if
      @action = :restart
      yield(self)
      @action = nil
    end
    
    # Instantiate a Condition of type +kind+ and pass it into the optional
    # block. Attributes of the condition must be set in the config file
    def condition(kind)
      # must be in a _if block
      unless @action
        abort "Watch#condition can only be called from inside a start_if block"
      end
      
      # create the condition
      begin
        c = Condition.generate(kind)
      rescue NoSuchConditionError => e
        abort e.message
      end
      
      # send to block so config can set attributes
      yield(c) if block_given?
      
      # call prepare on the condition
      c.prepare
      
      # abort if the Condition is invalid, the Condition will have printed
      # out its own error messages by now
      unless c.valid?
        abort
      end
      
      # inherit interval from meddle if no poll condition specific interval was set
      if c.kind_of?(PollCondition) && !c.interval
        if self.interval
          c.interval = self.interval
        else
          abort "No interval set for Condition '#{c.class.name}' in Watch '#{self.name}', and no default Watch interval from which to inherit"
        end
      end
      
      self.conditions[@action] << c
    end
        
    # Schedule all poll conditions and register all condition events
    def monitor
      [:start, :restart].each do |cmd|
        self.conditions[cmd].each do |c|
          @meddle.timer.register(self, c, cmd) if c.kind_of?(PollCondition)
        end
      end
    end
    
    def action(a, c)
      case a
      when :start
        puts self.start
        call_action(c, :start, self.start)
        sleep(self.grace)
      when :restart
        if self.restart
          puts self.restart
          call_action(c, :restart, self.restart)
        else
          action(:stop, c)
          action(:start, c)
        end
        sleep(self.grace)
      when :stop
        puts self.stop
        call_action(c, :stop, self.stop)
        sleep(self.grace)
      end      
    end
    
    def call_action(condition, action, command)
      # before
      (self.behaviors + [condition]).each { |b| b.send("before_#{action}") }
      
      # action
      if command.kind_of?(String)
        # string command
        system(command)
      else
        # lambda command
        command.call
      end
      
      # after
      (self.behaviors + [condition]).each { |b| b.send("after_#{action}") }
    end
  end
  
end