#--
#******************************************************************************
#* Copyright 1990-2007 MBARI
#* MBARI Proprietary Information. All rights reserved.
#******************************************************************************
#* Summary  : Contain the rover software component objects
#* Filename : rover.rb
#* Author   : Henthorn
#* Project  : Benthic Rover
#* Version  :
#* Created  :
#* Modified :
#******************************************************************************

require "getoptlong"
require "rover_utils"
require "rover_environment"
require "acm"
require "aanderaa"
require "relay_board"
require "video"
require "rack"
require "ezservo"
require "modem"
require "configman"
require "battery"
require "singleton"

# Class designed to capture and encapsulate the command-line options, then
# allow read-only access to the state (since the command-line cannot change
# after the program starts).
# The Rover instance will contain an instance of this class.  
#
class CmdLineOptions

  # List elements here to allow read access
  #
  attr_reader :config_file, :launch_time, :simple, :long_mission, :sim, :warp
  
  # Initialize all the elements to default states
  #
  def initialize
    @config_file = nil
    @launch_time = nil
    @long_mission = nil
    @simple = nil
    @sim = nil
    @warp = nil
  end

  def usage
    puts("Rover main program\nusage:")
    s = "\truby main.rb [{--config | -f} filename] [--sim | m] [--warp | -w] " +
        "[--simple | -s] [--long | -l] [{--launch_time | -t} time] " +
        "[--help | -h | -?]"
    puts(s)
    puts("\t[{--config | -f} filename]  Config file (default is RoverCfg.xml)")
    puts("\t[--sim | -m]                Run in simulation mode")
    puts("\t[--warp | -w]               Warp extended wait times to 1 second")
    puts("\t[--simple | -s]             Simple forward moves without turns")
    puts("\t[--long | -l]               Use long mission settings in config file")
    puts("\t[{--launch_time | -t} time] Override configured launch time")
    puts("\t[--help | -h | -?]          Print this usage text")
  end
    
  # Uses Ruby GetoptLong class to parse command-line. Set state of
  # elements based on values and switches on the command-line.
  # 
  def parse
  
    # List expected/allowed command-line options here
    #
    opts = GetoptLong.new(
      ["--config",       "-f",  GetoptLong::REQUIRED_ARGUMENT],
      ["--simple",       "-s",  GetoptLong::NO_ARGUMENT],
      ["--long",         "-l",  GetoptLong::NO_ARGUMENT],
      ["--launch_time",  "-t",  GetoptLong::REQUIRED_ARGUMENT],
      ["--sim",          "-m",  GetoptLong::NO_ARGUMENT],
      ["--warp",         "-w",  GetoptLong::NO_ARGUMENT],
      ["--help",   "-?", "-h",  GetoptLong::NO_ARGUMENT]
    )

    # Iterate through options, set state of elements
    #
    opts.each do |opt, argm|
      case opt
        # Save specified config file name, raise exception if file nonexistant
        #
        when '--config'
          @config_file = argm
          unless (File.file?(@config_file))
            raise("Exception! Specified config file not found: #{@config_file}")
          end
        # Get launch time in minutes. Value must be > 0.
        #
        when '--launch_time'
          @launch_time = argm.to_f
          unless (@launch_time > 0)
            raise("Exception! Launch time must be > 0: #{@launch_time}")
          end
        when '--simple'
          @simple = true
        when '--long'
          @long_mission = true
        when '--sim'
          @sim = true
        when '--warp'
          @warp = true
        when '--help'
          usage
          exit
      end
    end
  end
  
  def to_s
    s = ""
    s += ":Config file = #{@config_file}  " if @config_file 
    s += ":Launch time = #{@launch_time}  " if @launch_time
    s += ":Long mission = #{@long_mission}  " if @long_mission
    s += ":Simple mission = #{@simple}  " if @simple
    s += ":Sim = #{@sim}  " if @sim
    s += ":Warp time = #{@warp}  " if @warp
    s
  end  
end

#This class is a singleton, to access it call 'Rover.instance'
class Rover
  include Singleton
  
  # A constant that other classes can use to see if system is in test mode
  TEST = false

  attr_accessor :acm, :relay, :video, :rack, :motors, :ref_optode, \
                :modem, :port_optode, :starboard_optode, :cm, :rt, :clo, \
                :battery

  #in the future, this should be where a config file is read to initialize
  #all the rover's subsystems.
  def initialize
    # Get command-line options. Must be valid before proceeding.
    #
    begin
      self.clo = CmdLineOptions.new
      self.clo.parse()
    rescue Exception => e
      puts(e)
      exit
    end
    puts(self.clo)
    self.cm = ConfigMan.instance
    self.rt = RoverTime.instance

    if(self.clo.sim)
      initialize_sim()
    else
      self.relay = RelayMan.new          # Using RelayMan relay manager
      self.acm = Acm.new nil, "/dev/acm"
      self.port_optode = Aanderaa.new nil, "/dev/port_optode"
      self.starboard_optode = Aanderaa.new nil, "/dev/starboard_optode"
      self.ref_optode = Aanderaa.new nil, "/dev/ref_optode"
      self.motors = Ezservo.rover
      self.rack = Rack.rover
      self.modem = BenthosModem.rover
      self.battery = BattMan.new

      #LogHelper.latest="/cf/rover/logs/testlogs"
      #self.acm = Tcm2Compass.new nil

      if (ip = cm.get("VideoIPAddr"))
        self.video = Video.new(ip)
      else
        self.video = Video.new
      end

      self.rt.warp = @clo.warp
    end
  end

  def initialize_sim
    self.relay = SimRelayMan.new
    self.acm = SimAcm.new nil, "/dev/acm"
    self.port_optode = SimAanderaa.new nil, "/dev/port_optode"
    self.starboard_optode = SimAanderaa.new nil, "/dev/starboard_optode"
    self.ref_optode = SimAanderaa.new nil, "/dev/ref_optode"
    self.motors = SimEzservo.rover
    self.rack = SimRack.rover
    self.modem = SimModem.rover
    self.video = SimVideo.new
    self.battery = SimBattMan.new
    self.rt.warp = true
  end
end
