#ifndef __bingo_base_index__
#define __bingo_base_index__

#include "molecule/molecule_fingerprint.h"
#include "bingo_object.h"
#include "bingo_fp_storage.h"
#include "bingo_cf_storage.h"
#include "bingo_mmf_storage.h"
#include "bingo_mapping.h"
#include "bingo_properties.h"
#include "bingo_exact_storage.h"
#include "bingo_gross_storage.h"
#include "bingo_sim_storge.h"
#include "bingo_lock.h"

#define BINGO_VERSION "v0.72"

using namespace indigo;

namespace bingo
{
   class Matcher;
   class MatcherQueryData;

   class Index
   {
   public:
      typedef enum {MOLECULE, REACTION, UNKNOWN} IndexType;

      virtual Matcher* createMatcher (const char *type, MatcherQueryData *query_data, const char *options) = 0;

      virtual void create (const char *location, const MoleculeFingerprintParameters &fp_params, const char *options, int index_id) = 0;

      virtual void load (const char *location, const char *options, int index_id) = 0;

      virtual int add (IndexObject &obj, int obj_id, DatabaseLockData &lock_data) = 0;

      virtual void optimize () = 0;

      virtual void remove (int id) = 0;
   
      virtual const byte * getObjectCf (int id, int &len) = 0;

      virtual const char * getIdPropertyName () = 0;

      virtual const char * getVersion () = 0;

      virtual IndexType getType () const = 0;

      virtual ~Index () {};
   };

   class BaseIndex : public Index
   {
   private:   
      struct _Header
      {
         BingoAddr properties_offset;
         BingoAddr mapping_offset;
         BingoAddr back_mapping_offset;
         BingoAddr cf_offset;
         BingoAddr sub_offset;
         BingoAddr sim_offset;
         BingoAddr exact_offset;
         BingoAddr gross_offset;
         int object_count;
         int first_free_id;
      };

   public:
      virtual void create (const char *location, const MoleculeFingerprintParameters &fp_params, const char *options, int index_id);

      virtual void load (const char *location, const char *options, int index_id);
      
      virtual int add (IndexObject &obj, int obj_id, DatabaseLockData &lock_data);

      virtual void optimize ();

      virtual void remove (int id);

      const MoleculeFingerprintParameters & getFingerprintParams () const;

      TranspFpStorage & getSubStorage ();

      SimStorage & getSimStorage ();

      ExactStorage & getExactStorage ();
      
      GrossStorage & getGrossStorage ();

      BingoArray<int> & getIdMapping ();

      BingoMapping & getBackIdMapping ();

      ByteBufferStorage & getCfStorage ();

      int getObjectsCount () const;

      virtual const byte * getObjectCf (int id, int &len);

      virtual const char * getIdPropertyName ();

      virtual const char * getVersion ();

      virtual IndexType getType () const;

      static IndexType determineType (const char *location);

      virtual ~BaseIndex ();

   protected:
      BaseIndex (IndexType type);
      IndexType _type;
      bool _read_only;

   private:
      struct _ObjectIndexData 
      {
         Array<byte> sub_fp;
         Array<byte> sim_fp;
         Array<char> cf_str;
         Array<char> gross_str;
         dword hash;
      };

      MMFStorage _mmf_storage;
      BingoPtr<_Header> _header;
      BingoPtr< BingoArray<int> > _id_mapping_ptr;
      BingoPtr<BingoMapping> _back_id_mapping_ptr;
      BingoPtr<TranspFpStorage> _sub_fp_storage;
      BingoPtr<SimStorage> _sim_fp_storage;
      BingoPtr<ExactStorage> _exact_storage;
      BingoPtr<GrossStorage> _gross_storage;
      BingoPtr<ByteBufferStorage> _cf_storage;
      BingoPtr<Properties> _properties;
      
      MoleculeFingerprintParameters _fp_params;
      std::string _location;

      int _index_id;

      static void _checkOptions (std::map<std::string, std::string> &option_map, bool is_create);

      static size_t _getMinMMfSize (std::map<std::string, std::string> &option_map);

      static size_t _getMaxMMfSize (std::map<std::string, std::string> &option_map);

      static bool _getAccessType (std::map<std::string, std::string> &option_map);

      void _saveProperties (const MoleculeFingerprintParameters &fp_params, int sub_block_size, 
                            int sim_block_size, int cf_block_size, 
                            std::map<std::string, std::string> &option_map);

      bool _prepareIndexData (IndexObject &obj, _ObjectIndexData &obj_data);

      void _insertIndexData(_ObjectIndexData &obj_data);

      void _mappingCreate ();

      void _mappingLoad ();

      void _mappingAssign (int obj_id, int base_id);

      void _mappingAdd (int obj_id, int base_id);

      void _mappingRemove (int obj_id);
   };
};

#endif // __bingo_base_index__
