// This file is part of The New Aspell
// Copyright (C) 2001 by Kevin Atkinson under the GNU LGPL license
// version 2.0 or 2.1.  You should have received a copy of the LGPL
// license along with this library if you did not you can find
// it at http://www.gnu.org/.

#ifndef ASPELL_CONFIG___HPP
#define ASPELL_CONFIG___HPP

#include "can_have_error.hpp"
#include "key_info.hpp"
#include "posib_err.hpp"
#include "string.hpp"
#include "vector.hpp"
#include "string_map.hpp"

namespace acommon {

  class OStream;
  class KeyInfoEnumeration;
  class StringPairEnumeration;
  class MutableContainer;

  // The Config class is used to hold configuration information.
  // it has a set of keys which it will except.  Inserting or even
  // trying to look at a key that it does not know will produce
  // an error.  Extra accepted keys can be added with the set_extra 
  // method.

  // An r in the otherdata[0] value means that when merged into
  // a config of a different type it will be renamed to
  // <config name>_<name>
  // A p in the other datavalue means that is is a placeholder
  // for when a "r" is merged.  It should start with <config name>

  class Config;

  struct ConfigModule {
    const char * name;
    const KeyInfo * begin;
    const KeyInfo * end;
  };

  class Notifier {
  public:
    // returns a copy if a copy should be made otherwise returns null
    virtual Notifier * clone(Config *) const {return 0;}
    virtual ~Notifier() {}

    virtual PosibErr<void> item_updated(const KeyInfo *, bool)       {return no_err;}
    virtual PosibErr<void> item_updated(const KeyInfo *, int)        {return no_err;}
    virtual PosibErr<void> item_updated(const KeyInfo *, ParmString) {return no_err;}
    virtual PosibErr<void> item_added  (const KeyInfo *, ParmString) {return no_err;}
    virtual PosibErr<void> item_removed(const KeyInfo *, ParmString) {return no_err;}
    virtual PosibErr<void> all_removed (const KeyInfo *, ParmString) {return no_err;}
    // the second paramater for all_removed should not be used
  };

  class PossibleElementsEmul;
  class NotifierEnumeration;
  class GetLine;
  class MDInfoListofLists;

  struct ConfigKeyModuleInfo {
    const KeyInfo       * main_begin;
    const KeyInfo       * main_end;
    const KeyInfo       * extra_begin;
    const KeyInfo       * extra_end;
    const ConfigModule  * modules_begin;
    const ConfigModule  * modules_end;
  };

  class Config : public CanHaveError {
    // copy and destructor provided
    friend class MDInfoListofLists;
  private:
    String    name_;
    StringMap data_;

    bool attached_;    // if attached can't copy
    Vector<Notifier *> notifier_list;

    friend class PossibleElementsEmul;

    ConfigKeyModuleInfo kmi;

    int md_info_list_index;

    void copy_notifiers(const Config & other);
    void del_notifiers();

  public:
    
    String temp_str;

    ASPELL_API Config(ParmString name,
	   const KeyInfo  * mainbegin, 
	   const KeyInfo * mainend);

    ASPELL_API Config(const Config &);
    ASPELL_API ~Config();
    ASPELL_API Config & operator= (const Config &);

    ASPELL_API bool get_attached() const {return attached_;}
    ASPELL_API void set_attached(bool a) {attached_ = a;}

    ASPELL_API Config * clone() const;
    ASPELL_API void assign(const Config * other);

    ASPELL_API const char * name() const {return name_.c_str();}

    ASPELL_API NotifierEnumeration * notifiers() const;
  
    ASPELL_API bool add_notifier    (      Notifier *);
    ASPELL_API bool remove_notifier (const Notifier *);
    ASPELL_API bool replace_notifier(const Notifier *, Notifier *);

    ASPELL_API void set_extra(const KeyInfo * begin, const KeyInfo * end);

    ASPELL_API void set_modules(const ConfigModule * modbegin, const ConfigModule * modend);

    ASPELL_API static const char * base_name(ParmString name);
  
    ASPELL_API PosibErr<const KeyInfo *> keyinfo(ParmString key) const;

    ASPELL_API KeyInfoEnumeration * possible_elements(bool include_extra = true);

    ASPELL_API StringPairEnumeration * elements();
    
    ASPELL_API PosibErr<String> get_default (ParmString key) const;

    ASPELL_API PosibErr<String> retrieve    (ParmString key) const;
  
    ASPELL_API bool have (ParmString key) const;

    ASPELL_API PosibErr<void> retrieve_list (ParmString key, MutableContainer *) const;
    ASPELL_API PosibErr<bool> retrieve_bool (ParmString key) const;
    ASPELL_API PosibErr<int>  retrieve_int  (ParmString key) const;
    
    ASPELL_API PosibErr<void> replace (ParmString, ParmString);
    ASPELL_API PosibErr<bool> remove  (ParmString);
    
    ASPELL_API void write_to_stream(OStream & out, bool include_extra = false);

    ASPELL_API PosibErr<void> read_in_settings(const Config * override = 0);

    ASPELL_API PosibErr<void> read_in(IStream & in);
    ASPELL_API PosibErr<void> read_in_file(ParmString file);
    ASPELL_API PosibErr<void> read_in_string(ParmString str);

    ASPELL_API void merge(const Config &);
    //Note: if the same key is in both config's it is assumed that they
    // have the same data type.
  };

  ASPELL_API Config * new_config();
  ASPELL_API Config * new_basic_config(); // config which doesn't require any
			       // external symbols

  class NotifierEnumeration {
    // no copy and destructor needed
    Vector<Notifier *>::const_iterator i;
    Vector<Notifier *>::const_iterator end;
  public:
    NotifierEnumeration(const Vector<Notifier *> & b) 
      : i(b.begin()), end(b.end()) {}
    const Notifier * next() {
      const Notifier * temp = *i;
      if (i != end)
	++i;
      return temp;
    }
    bool at_end() const {return i == end;}
  };

  class KeyInfoEnumeration {
  public:
    typedef const KeyInfo * Value;
    virtual KeyInfoEnumeration * clone() const = 0;
    virtual void assign(const KeyInfoEnumeration *) = 0;
    virtual bool at_end() const = 0;
    virtual const KeyInfo * next() = 0;
    virtual ~KeyInfoEnumeration() {}
  };

}

#endif

