#include "bingo_exact_storage.h"

#include "molecule/elements.h"
#include "base_cpp/crc32.h"
#include "bingo_mmf_storage.h"

#include "graph/subgraph_hash.h"

using namespace bingo;

ExactStorage::ExactStorage ()
{
}

BingoAddr  ExactStorage::create(BingoPtr<ExactStorage> &exact_ptr)
{
   exact_ptr.allocate();
   new (exact_ptr.ptr()) ExactStorage();
         
   return (BingoAddr)exact_ptr;
}

void ExactStorage::load (BingoPtr<ExactStorage> &exact_ptr, BingoAddr offset)
{
   exact_ptr = BingoPtr<ExactStorage>(offset);
}


void ExactStorage::add( dword hash, int id )
{
   _molecule_hashes.add(hash, id);
}

void ExactStorage::findCandidates (dword query_hash, Array<int> &candidates, int part_id, int part_count)
{
   profTimerStart(tsingle, "exact_filter");

   dword first_hash = 0;
   dword last_hash = (dword)(-1);

   if (part_id != -1 && part_count != -1)
   {
      first_hash = (part_id - 1) * last_hash / part_count;
      last_hash = part_id * last_hash / part_count;
   }

   if (query_hash < first_hash || query_hash > last_hash)
      return;
   
   Array<size_t> indices;
   _molecule_hashes.getAll(query_hash, indices);

   for (int i = 0; i < indices.size(); i++)
      candidates.push(indices[i]);
}

dword ExactStorage::calculateMolHash (Molecule &mol)
{
   QS_DEF(Molecule, mol_without_h);
   QS_DEF(Array<int>, vertices);
   int i;

   vertices.clear();
   
   for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i))
      if (mol.getAtomNumber(i) != ELEM_H)
         vertices.push(i);

   mol_without_h.makeSubmolecule(mol, vertices, 0);

   QS_DEF(Array<int>, vertex_codes);
   vertex_codes.clear_resize(mol_without_h.vertexEnd());

   SubgraphHash hh(mol_without_h);

   for (int v = mol_without_h.vertexBegin(); v != mol_without_h.vertexEnd(); v = mol_without_h.vertexNext(v))
      vertex_codes[v] = _vertexCode(mol_without_h, v);
   hh.vertex_codes = &vertex_codes;
   hh.max_iterations = (mol_without_h.edgeCount() + 1) / 2;

   return hh.getHash();
}

dword ExactStorage::calculateRxnHash (Reaction &rxn)
{
   QS_DEF(Molecule, mol_without_h) ;
   QS_DEF(Array<int>, vertices);
   int i, j;
   dword hash = 0;

   for (j = rxn.begin(); j != rxn.end(); j = rxn.next(j))
   {
      Molecule &mol = rxn.getMolecule(j);

      vertices.clear();

      for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i))
         if (mol.getAtomNumber(i) != ELEM_H)
            vertices.push(i);

      mol_without_h.makeSubmolecule(mol, vertices, 0);
      SubgraphHash hh(mol_without_h);
      hash += hh.getHash();
   }

   return hash;
}

int ExactStorage::_vertexCode (Molecule &mol, int vertex_idx)
{
   if (mol.isPseudoAtom(vertex_idx))
      return indigo::CRC32::get(mol.getPseudoAtom(vertex_idx));

   if (mol.isRSite(vertex_idx))
      return ELEM_RSITE;

   return mol.getAtomNumber(vertex_idx);
}
