//  KVDataBase.h
//
//  Qdos - header file for Database of Key Values class 
//
//  Janet Barnes
//  Sept 2000
//

#ifndef Q_KVDATABASE_H
#define Q_KVDATABASE_H

#include "Exception.h"
#include "Var.h"
#include "Naming.h"
#include "QObject.h"
#include "Factory.h"
#include "StringRep.h"
#include "DataSource.h"
#define RENAME_ATTEMPTS 200
#define RENAME_EXTENSION "A"

namespace QDOS {

class KVDataBaseException :public Exception {
public:
  KVDataBaseException( ExceptionMsg m="" )
   : Exception(m<<" [KFDataBaseException]" ) {;}
};

class KVBadObject:public KVDataBaseException
{
public:
 KVBadObject(ExceptionMsg m=""):\
        KVDataBaseException(m<<"[KVBadObject]"){;}
};

class KVDataBase:public VarTarget
{
    typedef ObjectNamesTable::recursive_iterator recursive_iterator;    

    ObjectNamesTable_var _data_by_name;
public:
   typedef enum KVWriteOption_e {
     KV_NO_REPLACE=-1,
     KV_REPLACE
   } KVWriteOption;
   
   KVDataBase():_data_by_name(new ObjectNamesTable)
   {;}
   KVDataBase(const ObjectNamesTable& object_table):\
     _data_by_name(new ObjectNamesTable(object_table)){;}

   QdObject_var get(const string& name) const
   {
     QdObject_var obj;
     try{  obj = _data_by_name->resolve( name); }
     catch(NamingException){ }
     return obj;
   }
   
   void set(const string& name, QdObject_var obj,\
     KVWriteOption write_option=KV_NO_REPLACE)
   {
     QdObject_var tmp=get(name);
     bool not_found=tmp.is_nil();
     if(not_found || write_option==KV_REPLACE)
     {
       if(!not_found)
         erase(name);   
       _data_by_name->bindx(name,obj);
     }
     else
     {
        string new_name=get_new_name(name);
        try{
          _data_by_name->bindx(new_name,obj);
        }
        catch(Exception& e)
        { 
          throw KVBadObject(e.msg());
        }
      }
    } 
    
    string get_new_name(const string& name)
    {
      string new_name=name;
      int num=(int)RENAME_ATTEMPTS;
      string ext=RENAME_EXTENSION;
      for(int i=0;i<num;i++) 
      {
        new_name+=ext;
        try{
          QdObject_var obj=_data_by_name->resolve( new_name);
        }
        catch(NameNotFound& not_found)
        {
          return new_name;
        }
      }
    }
              
   template<class T>
   void set (const string& name, const T& t, \
     KVWriteOption write_option=KV_NO_REPLACE )
   {
     QdObject_var obj=Factory::clone(t);
     set(name, obj);
    }
    
    void erase(const string& name) 
    {
        try{  _data_by_name->unbind( name); }
        catch( NamingException ){ } 
    }
    
    void rename(const string& old_name, const string& new_name) 
    {
        QdObject_var obj=_data_by_name->resolve(old_name);
        _data_by_name->unbind(old_name);
        _data_by_name->bindx(new_name,obj);
    }
    
    void prune_empty_contexts() 
    {
        if(!_data_by_name->is_empty())
        {
            recursive_iterator itr=_data_by_name->recursive_end();
            BindingType bindtype;
            do
            {
                itr--;
                bindtype=itr.type();
                if(bindtype==N_CONTEXT)
                {
                    bool remove=false;
                    if(itr->second._nc.is_nil())
                        remove=true;
                    else if(itr->second._nc->is_empty())
                        remove=true;
                    if(remove)
                    {
                      _data_by_name->unbind(itr.name());
                      itr=_data_by_name->recursive_end();
                    }
                }
            } while (itr!=_data_by_name->recursive_begin());
        }
     }
    
    void diag_print() const
    {
        StringRep stringrep;
        recursive_iterator itr;
        BindingType bindtype;
        for( itr=_data_by_name->recursive_begin();\
            itr!=_data_by_name->recursive_end();itr++)
        {
            cout << itr.name();
            bindtype=itr.type();
            // we need to define object_srep() for each Qd object type
            // and a diag_print();
            if(bindtype==N_OBJECT)
            {
                cout << " = " << "** QObject_var **" << endl;
                string obj_srep=stringrep.object_type(itr->second._obj);
                cout << "QObject type: " << obj_srep << endl;
                cout << "QObject: " << stringrep(itr->second._obj) << endl;
            }
        }
        
     }
     
     DataNamesTable_var get_names() const
     {
       DataNamesTable_var data_names=new DataNamesTable;
       for(recursive_iterator itr=_data_by_name->recursive_begin();\
         itr!=_data_by_name->recursive_end();itr++)
       {
         if(itr.type()==N_OBJECT)
             data_names->bindx( itr.name(), "" );
       }
       return data_names;
     }
   
}; // end KVDataBase

typedef Var<KVDataBase> KVDataBase_var;

} // end namespace QDOS


#endif //  #ifndef Q_KVDATABASE_H
