

/**********************************************************//**
 **
 ** @file base/config.h
 **
 ** Copyright (C) 2010  Xpace, LLC.  All rights reserved
 **
 ** www.xpace.net
 **
 **************************************************************/


#ifndef XPACE_CONFIG_H
#define XPACE_CONFIG_H

#ifndef _WIN32
#include <stdint.h>
#endif

#include "base/types.h"
#include "base/exception.h"

namespace Xpace
{
  /// Each high-level Xpace object has a Configuration
  class XPACE_EXPORT Configuration
  {
  public:
    /// create a configuration from a fragment of XML
    /// @param str the XML fragment
    /// @throw XML_Error
    Configuration
      (const String& str = String());   

    #if defined XPACE_FILE_H || defined DOCUMENTATION
    /// create a configuration from a configuration File
    /// @param file the configuration File
    /// @param tag search the Configuration for a sub-configuration with this tag (cf. findTag)
    /// @throw File_Cant_Read
    /// @throw XML_Error
    /// @throw XML_No_Value if tag is not found
    Configuration
      (File* file,
       const String& tag = String());   
    #endif

    Configuration
      (const Configuration&);
    Configuration& operator=
      (const Configuration&);

    /// @return true if empty
    bool operator!
      ()
      const;

    /// @return the XML representation of the entire Configuration
    String toString
      ()
      const;

    #if defined QDOM_H || defined DOCUMENTATION
    /// Convert a QDom to a Configuration
    /// @param qdom the QDom to be converted
    Configuration
      (const QDomElement& qdom);

    /// Convert a Configuration to a QDom
    /// @return the QDom
    operator const QDomElement&
      ()
      const;
    #endif

    /// @return this Configuration's XML tag
    String getTag
      ()
      const;

    /// Copy a sub-configuration
    /// @param tag search the Configuration for a sub-configuration with this tag
    /// @param name search for a sub-configuration with this attribute or child
    /// @param value search for a sub-configuration with an attribute/child called name with this value.
    /// @return a copy of the sub-configuration
    Configuration findTag
      (const String& tag,
       const String& name = String(),
       const String& value = String())
      const;

    /// get a child's value or an attribute
    /// search for attribute first, then child value

    /// @param tag the tag whose value we want; separate >1 tags with '|'
    /// @return the (String) value
    String getValue
      (const String& tag)
      const;

    /// @param tag the tag whose value we want
    /// @param def the default value if not found
    /// @param ok fillin true if found
    /// @return the integer value
    int64 getValueInt
      (const String& tag,
       int64 def = 0,
       bool* ok = 0)
      const;

    /// @param tag the tag whose value we want
    /// @return the (boolean) value
    bool getValueBool
      (const String& tag)
      const;

    /// get named values, starting at root
    /// @param name depth-first search for this tag
    /// @return the values associated wuth this tag, delimited by '.'
    String getValuePath
      (const String& tag)
      const;

    // find children only, not attributes

    /// @param tag the tag whose value we want
    /// @return the (String) value
    String getChild
      (const String& tag)
      const;

    /// @param tag the tag whose value we want
    /// @param def the default value if not found
    /// @param ok fillin true if found
    /// @return the integer value
    int64 getChildInt
      (const String& tag,
       int64 def,
       bool* ok)
      const;

    /// @param tag the tag whose value we want
    /// @return the (boolean) value
    bool getChildBool
      (const String& tag)
      const;

    /// set attribute values

    /// set this Configuation's XML tag
    /// @param tag the new tag
    void setTag
      (const String& tag);

    /// @param name set this attribute
    /// @param value to this (String) value
    void setValue
      (const String& name,
       const String& value);

    /// @param name set this attribute
    /// @param value to this (integer) value
    void setValue
      (const String& name,
       int64 value);

    /// @param tag set this attribute
    /// @param value to this (boolean) value
    void setValue
      (const String& name,
       bool value);
    
    /// traverse calls this for each XML element in the Configuration
    struct doThis
    {
      virtual ~doThis() 
      { 
      }

      /// @param the current sub-configuration 
      /// @return true to continue, false to stop
      virtual bool operator()
        (const Configuration& config)
      = 0;
    };

    /// @param act called for each XML element in the Configuration
    /// @param maxDepth go no deeper than this
    /// @return true if completed, false if stopped by callback
    bool traverse
      (doThis* act,
       uint maxDepth = uint(-1))
      const; 

    #if defined XPACE_FILE_H || defined DOCUMENTATION
    /// serialize the Configuration
    /// @param f to this file
    /// @return true if successfully serialized
    bool write
      (File* f)
      const;
    #endif

  private:
    intptr_t config;
    mutable String config_string;  
    void make_string
      ()
      const;
    bool check_bool
      (const String& val)
      const;
  };

  /// base class for a configurable object
  /// inherit from this to make your object configurable
  class XPACE_EXPORT Configurable
  {
  public :
    /// @return this object's Configuration
    const Configuration& getConfig
      ()
      const;

  protected :
    Configurable
      (const Configuration& c = Configuration(),
       String tag = String());
    Configuration config;
  };

  /// Configuration errors

  /// XML parse error
  class XML_Error : public Exception
  {
  public :
    /// @param error an English description of the error
    /// @param source the source XML
    /// @line the line number on which the error occurred
    /// @col the column in which the error occurred
    XML_Error
      (const String error,    
       const String source,
       uint line,
       uint col);
  };

  /// Can't find a named value
  class XML_No_Value : public Exception
  {
  public :
    /// @param name the name of the value 
    /// @param source the source XML
    /// @param the exception cauing the failure, if any
    XML_No_Value
      (const String name,
       const String source,
       const Exception reason = Exception());
  };

  /// Found a value, but it's bad (e.g., out of range, meaningless)
  class XML_Bad_Value : public Exception
  {
  public :
    /// @param name the name of the value 
    /// @param value the bad value
    /// @param source the source XML
    XML_Bad_Value
      (const String name,
       const String value,
       const String source);
  };
}

#endif

