##############################################################################
# ConfigMan - load mission config file and serve-up the values by name
#
#  23-May-07 RGH => Initial development, very simple document layout and
#                   server. Exceptions are caught during the load method
#                   and are not propagated.
#
#  31-Aug-07 RGH => Singleton class
#
##############################################################################

require 'rover_utils'
require 'rexml/document'    # Ruby XML library

class ConfigMan
  include Singleton
  include REXML
  include LogHelper

  # Constructor takes no arguments
  #
  def initialize
    @xml = nil
    @filename = nil
    @err = nil
  end

  # Change the configuration file used by ConfigMan object
  # Remember to call load again
  #
  def configfile=newfile
    @filename = newfile
  end
  
  def configfile
    @filename
  end
  
  def last_error
    @err
  end
  
  # Load method opens and parses the configuration file
  # Returns true if file can be opened and parsed
  # without error, otherwise nil. If nil, the exception
  # raised can be calling last_error
  #
  def load
    return nil unless @filename
    begin
      file = File.new(@filename)

      @xml = Document.new(file)
      file.close
      @err = nil

      true
            
    rescue Exception
      @err = $!
      file.close if file
      nil
    end

  end

  def load_config_item(item_name,      # string \
		       default_val=nil # if nil, exception if item not found \
		       )
    ci = nil
    return ci unless item_name
    
    ci = Rover.instance.cm.get(item_name)
    if (default_val && !ci)
      ci = default_val
    elsif (!ci)
      raise("Exception! #{item_name} not found in #{@filename}")
    end

    ci
  end

  # Get method returns the value for the given element.
  # Takes a string argument element name,
  # and the element attribute.
  # Returns nil if element could not be found
  # otherwise the string value found in the config file.
  #
  def get(elem_name, attr_name="value")
    return nil unless @xml && @xml.root

    # Use the pattern-matching feature in the 'each' method
    # Relies on the fact that all the elements with values are
    # in the 3rd level and have unique names, which allows us to
    # use "*/*/name" as the pattern. Grab the given attribute.
    #
    @val = nil
    @xml.elements.each("*/*/" + elem_name) { |e| @val = e.attributes[attr_name] }

    if (@val == nil)
      syslog("ConfigMan - no element/attr match for #{elem_name}/#{attr_name}")
    else
      syslog("ConfigMan - found #{elem_name}/#{attr_name} = #{@val}")
    end

    @val
  end
  
  # set_state method writes the value for the given element.
  # Takes a string arguments for the new value, the element name,
  # and the element attribute.
  # Returns nil if element could not be found, else the old value.
  # otherwise the string value is placed into the loaded config file.
  # The configuration is not saved, call write to do that.
  #
  def set_state(new_val, state_name, attr_name="value")
    return nil unless @xml && @xml.root

    # Use the pattern-matching feature in the 'each' method
    # Relies on the fact that all the elements with values are
    # in the 3rd level and have unique names, which allows us to
    # use "*/*/name" as the pattern. Grab the given attribute.
    #
    elem = nil
    @xml.elements.each("*/State/" + state_name) { |elem| @val = elem.attributes[attr_name] }

    if (@val == nil)
      syslog("ConfigMan - no element/attr match for #{state_name}/#{attr_name}")
    else
      syslog("ConfigMan - setting #{state_name}/#{attr_name} = #{new_val}")
      elem.attributes[attr_name] = new_val
    end

    @val
  end

  # This method writes the current state of the xml document
  # to the configuration file.
  # HACK: After writing to the file, reload it to ensure that
  # the file was saved before the system sleeps.
  #
  def write
    return nil unless @filename
    file = File.new(@filename, "w+")

    return nil unless file

    # Write the doc
    #
    @xml.write(file, 0)
    file.close
    sleep(1)
    syslog("_ config file saved")

    # Reload hack
    #
    self.load
    sleep(1)
    syslog("_ config file reloaded")
    sleep(1)
    return load
    syslog("_ config file reloaded again")
  end
  
end

