/***************************************************************************
 $RCSfile: conf.h,v $
                             -------------------
    cvs         : $Id: conf.h,v 1.8 2003/07/02 18:22:24 aquamaniac Exp $
    begin       : Tue Dec 13 2001
    copyright   : (C) 2001 by Martin Preuss
    email       : martin@aquamaniac.de

 ***************************************************************************
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Lesser General Public            *
 *   License as published by the Free Software Foundation; either          *
 *   version 2.1 of the License, or (at your option) any later version.    *
 *                                                                         *
 *   This library is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
 *   MA  02111-1307  USA                                                   *
 *                                                                         *
 ***************************************************************************/




/*

 */

#ifndef C_CONFIG_H
#define C_CONFIG_H

#include <openhbci/parser.h>

/* types of config nodes */
#define CONFIG_TYPE_ROOT  0
#define CONFIG_TYPE_GROUP 1
#define CONFIG_TYPE_VAR   2
#define CONFIG_TYPE_VALUE 3


/* mode flags */
#define CONFIG_MODE_QUOTED_NAME            0x00000100
#define CONFIG_MODE_QUOTED_VALUES          0x00000200
#define CONFIG_MODE_UNIQUE_VARS            0x00000400
#define CONFIG_MODE_IGNORE_GROUPS          0x00000800
#define CONFIG_MODE_IGNORE_VARS            0x00001000
#define CONFIG_MODE_RETURN_ON_ERROR        0x00002000
#define CONFIG_MODE_BLANK_CONTINUATION     0x00004000
#define CONFIG_MODE_OVERWRITE_VARS         0x00008000
#define CONFIG_MODE_STOP_ON_BLANK_LINE     0x00010000
#define CONFIG_MODE_WRITE_GROUP_NAME       0x00020000
#define CONFIG_MODE_WRITE_EMPTY_GROUP      0x00040000
#define CONFIG_MODE_IGNORE_GROUP_CASE      0x00080000
#define CONFIG_MODE_IGNORE_VAR_CASE        0x00100000
#define CONFIG_MODE_UNIQUE_GROUPS          0x00200000
#define CONFIG_MODE_OVERWRITE_GROUPS       0x00400000
#define CONFIG_MODE_ALLOW_EMPTY_VALUES     0x00800000
#define CONFIG_MODE_COLON                  0x01000000
#define CONFIG_MODE_BACKSLASH_CONTINUATION 0x02000000
#define CONFIG_MODE_ONE_VALUE_PER_LINE     0x04000000
#define CONFIG_MODE_CREATE_EMPTY_VARS      0x08000000


#define CONFIG_MODE_DEFAULT (\
    PARSER_FLAGS_SKIP_BLANKS            | \
    PARSER_FLAGS_CUT_BLANKS             | \
    PARSER_FLAGS_REMOVE_QUOTATION_MARKS | \
    CONFIG_MODE_QUOTED_VALUES           | \
    CONFIG_MODE_UNIQUE_VARS             | \
    CONFIG_MODE_OVERWRITE_VARS          | \
    CONFIG_MODE_RETURN_ON_ERROR         | \
    CONFIG_MODE_WRITE_GROUP_NAME        | \
    CONFIG_MODE_IGNORE_GROUP_CASE       | \
    CONFIG_MODE_IGNORE_VAR_CASE         | \
    CONFIG_MODE_UNIQUE_GROUPS           | \
    CONFIG_MODE_ALLOW_EMPTY_VALUES      | \
    CONFIG_MODE_BACKSLASH_CONTINUATION  )

#define CONFIG_MODE_HTTP (\
    PARSER_FLAGS_SKIP_BLANKS            | \
    PARSER_FLAGS_CUT_BLANKS             | \
    PARSER_FLAGS_REMOVE_QUOTATION_MARKS | \
    CONFIG_MODE_UNIQUE_VARS             | \
    CONFIG_MODE_RETURN_ON_ERROR         | \
    CONFIG_MODE_WRITE_GROUP_NAME        | \
    CONFIG_MODE_IGNORE_GROUP_CASE       | \
    CONFIG_MODE_IGNORE_VAR_CASE         | \
    CONFIG_MODE_UNIQUE_GROUPS           | \
    CONFIG_MODE_ALLOW_EMPTY_VALUES      | \
    CONFIG_MODE_COLON                   | \
    CONFIG_MODE_STOP_ON_BLANK_LINE      | \
    CONFIG_MODE_ONE_VALUE_PER_LINE      | \
    CONFIG_MODE_IGNORE_GROUPS           | \
    CONFIG_MODE_BLANK_CONTINUATION      )



#include <string>
#include <openhbci/tree.h>
#include <openhbci/error.h>
#include <openhbci/stream.h>

namespace HBCI {

/**
 * @short Internal data class for Config
 * @author Martin Preuss<martin@aquamaniac.de>
 */
class DLLIMPORT ConfigNode {
public:
  int type;
  string data;
  ConfigNode(): type(CONFIG_TYPE_ROOT),data("ROOT") {
  };

  ConfigNode(int t, string d=""): type(t),data(d) {
  };

  ~ConfigNode() {} ;
};


/**
 * @short Internal data class for Config
 * @author Martin Preuss<martin@aquamaniac.de>
 */
class DLLIMPORT ConfigValue: public ConfigNode {
private:
public:
  ConfigValue(string s=""): ConfigNode(CONFIG_TYPE_VALUE,s) {};
  ~ConfigValue() {};
};


/**
 * @short Internal data class for Config
 * @author Martin Preuss<martin@aquamaniac.de>
 */
class DLLIMPORT ConfigVariable: public ConfigNode {
private:
public:
  ConfigVariable(string s=""): ConfigNode(CONFIG_TYPE_VAR,s) {};
  ~ConfigVariable() {};
};


/**
 * @short Internal data class for Config
 * @author Martin Preuss<martin@aquamaniac.de>
 */
class DLLIMPORT ConfigGroup: public ConfigNode {
private:
public:
  ConfigGroup(string s=""): ConfigNode(CONFIG_TYPE_GROUP,s) {};
  ~ConfigGroup() {};
};


/** 
 * @short Helper class to make maintaining a configuration and
 * saving/loading it a lot easier.
 *
 * @author Martin Preuss<martin@aquamaniac.de> */
class DLLIMPORT Config {
private:
  Tree<ConfigNode> _cfg;
  unsigned int _mode;
  string _linebuffer;
  Tree<ConfigNode>::iterator _parsewhere;
  Tree<ConfigNode>::iterator _parseroot;

  Error _parseValues(string &s,
                       unsigned int pos,
                       Tree<ConfigNode>::iterator curr);

  Error _parseVar(string &s,
                    Tree<ConfigNode>::iterator where);

  Error _parseLine(string &s);

  /**
   * Parses a group name from "[NAME]"
   * @author Martin Preuss<martin@aquamaniac.de>
   */
  Error _parseGroup(string &s,
                      Tree<ConfigNode>::iterator &curr);

  Error _writeVar(Stream *st,
                    Tree<ConfigNode>::const_iterator where) const;

  Error _writeGroup(Stream *st,
                      Tree<ConfigNode>::const_iterator where) const;

  /**
   * Searches for a group using the given iterator.
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return iterator pointing to the group found. If nothing found then that
   * iterator is invalid (isValid() will return false).
   */
  Tree<ConfigNode>::const_iterator
    _findGroup(string name,Tree<ConfigNode>::const_iterator where) 
      const;
  Tree<ConfigNode>::iterator
    _findGroup(string name,Tree<ConfigNode>::iterator where);

  /**
   * Searches for a variable using the given iterator.
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return iterator pointing to the variable found. If nothing found then that
   * iterator is invalid (isValid() will return false).
   */
  Tree<ConfigNode>::const_iterator
    _findVariable(string name,Tree<ConfigNode>::const_iterator where) 
      const;
  Tree<ConfigNode>::iterator
    _findVariable(string name,Tree<ConfigNode>::iterator where);

  /**
   * Adds a group as a child to the group that is selected by the given
   * iterator and returns an iterator with the newly created group selected.
   * You can only add groups to other group or to the root.
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return new iterator pointing to the new group (is invalid on error)
   * @param name name of the group to create
   * @param where iterator pointing to the group where to add the new group
   */
  Tree<ConfigNode>::iterator _addGroup(string name,Tree<ConfigNode>::iterator where);

  /**
   * Adds a variable as a child to the group that is selected by the given
   * iterator and returns an iterator with the newly created var selected.
   * You can only add variables to groups or to the root.
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return new iterator pointing to the new var (is invalid on error)
   * @param name name of the variable to create
   * @param where iterator pointing to the group where to add the new var
   */
  Tree<ConfigNode>::iterator
    _addVariable(string name,Tree<ConfigNode>::iterator where);


public:
  Config():_cfg(ConfigNode()),_mode(CONFIG_MODE_DEFAULT){};
  ~Config(){};

  /* some basic methods */
  /**
   * Returns an iterator pointing to the root of this config.
   * @author Martin Preuss<martin@aquamaniac.de>
   */
  Tree<ConfigNode>::iterator root() { return _cfg.root(); };
  Tree<ConfigNode>::const_iterator const_root() const 
      { return _cfg.const_root(); };

  /**
   * This method tries to find the path given. A path can contain group an variable names.
   * Valid paths are:
   * - "/"
   * - "group1/"
   * - "var1"
   * - "group1/group2/"
   * - "group1/var1"
   * - "group1/var1.var2"
   * - "/group1/" <BR>
   * and so on. As you can see a path to a group is indicated by a trailing "/". If the last character
   * is NOT a slash, then it is assumed that the path leads to a variable. As you may have noticed variables
   * may have sub-variables, but groups BELOW variables are NOT allowed.
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return iterator pointing to the group/var  found. If nothing found then that
   * iterator is invalid (isValid() will return false).
   * @param path the path to look for
   * @param where an iterator pointing to the node to search under
   * @param create if true then the path will be created, if not present
   */
  Tree<ConfigNode>::iterator
    findPath(string path,
             Tree<ConfigNode>::iterator where,
             bool create=false);

  Tree<ConfigNode>::const_iterator
    findPath(string path,
             Tree<ConfigNode>::const_iterator where) const;

  /**
   * This method tries to find a group by the given path. For a discussion about paths see @ref findPath().
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return iterator pointing to the group/var  found. If nothing found then that
   * iterator is invalid (isValid() will return false).
   * @param path to the group to find
   * @param where an iterator pointing to the node to search under
   * @param create if true then the path will be created, if not present
   */
  Tree<ConfigNode>::iterator
    findGroup(string path,
              Tree<ConfigNode>::iterator where,
              bool create=false);

  Tree<ConfigNode>::const_iterator
    findGroup(string path,
              Tree<ConfigNode>::const_iterator where) const;

  /**
   * This method tries to find a variable by the given path. For a discussion about paths see @ref findPath().
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return iterator pointing to the group/var  found. If nothing found then that
   * iterator is invalid (isValid() will return false).
   * @param path to the variable to find
   * @param where an iterator pointing to the node to search under
   * @param create if true then the path will be created, if not present
   */
  Tree<ConfigNode>::iterator
    findVariable(string path,
                 Tree<ConfigNode>::iterator where,
                 bool create=false);

  Tree<ConfigNode>::const_iterator
    findVariable(string path,
                 Tree<ConfigNode>::const_iterator where) const;

  /**
   * Selects or creates a variable according to the current mode.
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return iterator pointing to the var found/created. If nothing found then that
   * iterator is invalid (isValid() will return false).
   * @param name to the variable to create
   * @param where an iterator pointing to the node to search/create under
   */
  Tree<ConfigNode>::iterator createVar(string name,
                                           Tree<ConfigNode>::iterator where);

  /**
   * Selects or creates a group according to the current mode.
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return iterator pointing to the var found/created. If nothing found then that
   * iterator is invalid (isValid() will return false).
   * @param name to the variable to create
   * @param where an iterator pointing to the node to search/create under
   */
  Tree<ConfigNode>::iterator createGroup(string name,
                                             Tree<ConfigNode>::iterator where);

  /**
   * Sets the mode for this config. You can OR all PARSER_FLAGS and all
   * CONFIG_MODEs.
   * @author Martin Preuss<martin@aquamaniac.de>
   * @param mode mode to set
   */
  void setMode(unsigned int mode) { _mode=mode;};

  /**
   * Returns the current mode.
   * @author Martin Preuss<martin@aquamaniac.de>
   */
  unsigned int mode() const { return _mode;};

  /**
   * Call this method when you want to start parsing a configuration
   * (e.g. before reading a config file). This method sets up some internal data.
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return a Error object (its method isOk() will return false on error)
   * @param where iterator that points to the position you want to insert a new config
   */
  Error beginParsing(Tree<ConfigNode>::iterator where);

  /**
   * Call this method to let this class digest your configuration.
   * It takes care of the desired line continuation modes (like escaping lines by baskslash as in many
   * linux config files, or by escaping lines by leading blanks in the follwoing line as in HTTP headers).
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return a Error object (its method isOk() will return false on error)
   * @param s reference to the string containing you config data
   * (e.g. "variable=value", or "[group]")
   */
  Error parseLine(string &s);

  /**
   * Call this if you are finished with reading a configuration. This way the last line which might be
   * in internal buffers gets parsed.
   * @author Martin Preuss<martin@aquamaniac.de>
   * @return a Error object (its method isOk() will return false on error)
   */
  Error endParsing();


  Error readFromStream(Stream *st,
                         Tree<ConfigNode>::iterator where);

  Error writeToStream(Stream *st,
                        Tree<ConfigNode>::iterator where);

  void dumpCfg(Tree<ConfigNode>::const_iterator where, int level);

  void clear();

};


typedef Tree<ConfigNode>::iterator cfgPtr;

} /* namespace HBCI */

#endif





