=begin
 ******************************************************************************
 * Copyright 1990-2011 MBARI
 * MBARI Proprietary Information. All rights reserved.
 ******************************************************************************
 * Summary  : Implemetation of the Pause behavior
 * Filename : pause.rb
 * Author   : Henthorn
 * Project  : Sedimentaion Event Sensor
 * Version  : 1
 * Created  : Aug 11
 * Modified :
 ******************************************************************************
=end

# Refer to code home directory as base
#
require "#{ENV['ROVER_HOME']}/utils/rover_environment"
#

require "utils/misc"
require "utils/datalog"
require "control/behavior"
require "control/supervisor"
require "device/ezstepper"
# require 'device/SES'

##########
# Pause class.
# Pause (sleep) for a number of minutes
#
#
class Pause < Behavior

  #####
  # Accept a behavior element and extract parameters
  #
  def initialize(p_behavior_element)
    super

    @pause = 1   #minutes to hibernate for
    @delay = 0   #minutes to delay for before hibernating
    @updated_pause = nil
    @interval = false
    @awake = false

    load_attrs(p_behavior_element)
  end
  #####


  #####
  # Iterate over attributes in the element, extract parameters.
  #
  def load_attrs(p_behavior_element)
    super

    # Keep for later use
    #
    attrs = p_behavior_element.attributes
    attrs.keys.each do |k|
      case k

      when "hibernate_for"
        @pause = attrs[k].to_i

      when "delay_for"
        @delay = attrs[k].to_i

      when "interval"
        @interval = (attrs[k].downcase == "true")

      # When using awakw == true, ensure that the hibernate_for is significantly
      # less than the wakey timeout (usually 30 minutes)
      when "awake"
        @awake = (attrs[k].downcase == "true")

      else
        puts("Unrecognized attr: #{k}")

      end
    end

    # Option: Throw an exception if there any required attributes
    # missing
    #
  end
  #####


  #####
  # Initialize behavior and make ready for execution.
  # Should be called just before executing the behavior.
  #
  def init(p_seq_number=1)   # Where in the plan behavior sequence I am
    syslog("_ #{@name} init")

    # Get updated pause time from mission params
    # The key to use is the name of the behavior (either 'collection'
    # or 'flow_thru' initially)
    #
    if (@name == 'collection' || @name == 'flow_thru')
      bs = Supervisor.instance.dbif.load_mission_params()
      if (bs)
        ud = bs[@name]
        @updated_pause = ud.to_i if (ud)
        syslog("_ Using updated pause of #{@updated_pause}")
      end
    end

    # Perform initialization procedures
    @state = READY
  end
  #####


  #####
  def save_behavior_state()
    bs = Hash['type', self.class.to_s,
              'name', @name,
              'log_dir', DataLog.behavior_data_home]
    Supervisor.instance.deployment.dbif.save_behavior_data(bs)
  end
  #####

  #####
  # Resume from hibernation
  #
  def resume_init()
    syslog("_ resuming from #{@pause} minute hibernation")
    bs = Supervisor.instance.deployment.dbif.load_behavior_data
    type = bs['type']
    unless (type == self.class.to_s)
      raise "Cannot resume! Mismatched behavior types: #{type}"
    end

    name = bs['name']
    unless (name == @name)
      raise "Cannot resume! Mismatched behavior names: #{name}!=#{@name}"
    end

    DataLog.set_behavior_data_home = bs['log_dir']

    type = bs['type']
    @state = READY
    @resume = true
  end
  #####


  #####
  # Execute the behavior
  #
  def execute
    # Execute if ready.
    unless (@state == READY)
      syslog("! #{name} not in ready state")
      return
    end

    syslog("_ #{@name} executing")
    @state = EXECUTING

    ##
    # If we're resuming a deployment, then we're done with this behavior
    #
    if (@resume)
      console("_ Resuming Pause behavior")
      syslog("_ Back from pause, done")
      @resume = false
      @state = FINISHED
      return
    end

    #
    # Perform the behavior
    #
    begin
      # Use the updated pause time if requested.
      # Updated pause is acquired by the executive during
      # modem comms.
      #
      @pause = @updated_pause if (@updated_pause)

      syslog("_ Delaying for #{@delay} minutes")
      sleep(@delay*60)

      # If this is an interval pause, look at the last pause time
      # and adjust the programmed pause value.
      #
      boot_overhead = 58   # seconds
      sleep_time = 0
      if (@interval)

        power_up = Time.now.to_i + @pause*60 - boot_overhead  # default

        pause_data = Supervisor.instance.deployment.dbif.load_pause_data()

        if (pause_data && Options.instance.resume)
          last_time = pause_data['timestamp']                # seconds
          syslog("_ there is pause data: #{last_time.to_i}")

          # Calculate when I should request power up.
          # power_up = last_time + @delay*60 + @pause*60 - boot_overhead
          #
          # power_up = last_time + @delay*60 + @pause*60
          syslog("_ power_up = #{power_up}")
          delta = power_up - Time.now.to_i
          syslog("_ power_up delta is #{delta} seconds")

          # Split into minutes and seconds because wakey board
          # only deals in minutes.
          #
          if (delta > 0)
            delta_min = delta / 60
            delta_sec = delta % 60
          else
            delta_min = delta_sec = 0
          end
          @pause = delta_min
          sleep_time = delta_sec
          syslog("_ sleep #{delta_sec} seconds and hibernate for #{@pause} minutes")

        else
          # First interval pause case.
          # Adjust for the boot overhead.
          #
          syslog("_ first pause")
          pause_data = Hash.new   # first time
          @pause -= 1
          sleep_time = 60 - boot_overhead
        end

        # Save the current pause data
        #
        pause_data['timestamp'] = power_up
        Supervisor.instance.deployment.dbif.save_pause_data(pause_data)

        # Sleep for the seconds portion of the total time.
        #
        syslog("_ sleep #{sleep_time} seconds")
        sleep(sleep_time) if (sleep_time > 0)
      end

      syslog("_ Hibernate #{@pause} minutes")

      # Throw the Hibernate exception if @pause is at least 1 minutes.
      # Otherwise just sleep and continue.
      #
      self.save_behavior_state()
      # if (Supervisor.instance.deployment.hibernate && @pause > 1)
      if (Supervisor.instance.deployment.hibernate && @awake != true)
        raise Hibernate.new(@pause)
      end

      sleep(@pause*60) if (@pause > 0)

      # sleep(boot_overhead) if (@interval)

    ensure
      self.clean_up()
    end
  end
  #####


  #####
  def clean_up
    Rover.instance.relay.allOff
#    Rover.instance.wakey.v12_off
    Rover.instance.wakey_msg("v12_off")
  end
  #####


  #####
  # Abort the behavior
  #
  def abort
    syslog("_ #{@name} abort")
    if (@state == EXECUTING)
      @state = ABORTING

      # Kill the run thread, clean up
      @my_thread.kill if @my_thread
      clean_up
    else
      syslog("_ #{@name} not executing, aborting...")
    end
  end
  #####


  #####
  # Test method. Use to run standard test of MyClass from
  # a ruby script.
  #
  def MoveCarousel.test
    require 'rexml/document'
    # require 'device/ses'

    # Test code here
    move_xml = REXML::Document.new(File.new(Misc.find_plan("test_carousel.xml")))
    lc = MoveCarousel.new(move_xml.root)
    lc.init
    lc.execute
    puts("Done")
  end
  #####

end

#####
# Standalone unit test (recommended)
# Include at the end of the file as a hook to run a unit test of
# the code from the command line including the command line options
# (e.g., "$ ruby my_class.rb --sim").
#
if __FILE__ == $0
  MoveCarousel.test
end
