#include "dbhmm.h"

dbhmm::dbhmm()
{
  classdb = NULL;
}

dbhmm::~dbhmm()
{
  close();
}

int dbhmm::close()
{
  for( std::map<int, hmm*>::iterator i = myhmms.begin();
       i != myhmms.end(); i++ )
    delete i->second;
  myhmms.clear();
  return 0;
}

int dbhmm::init( classifierdb *sqlitedb, int id,
                 int fvid, int cvid, int del )
{
  close();
  classdb = sqlitedb;
  if( del == 0 )   
    {
      myid = id;
      loadmodels();
    }
  else
    {
      myfvid = fvid;
      mycvid = cvid;
      time_t ct;
      time( &ct );
      std::string createdate;
      createdate = ctime( &ct );
      myid = classdb->add_model( mycvid, myfvid, createdate );
      if( myid < 0 )
        {
          std::cout << "ERROR on add mondel\n";
          exit(0);
        }
    }
  return 0;
}

int dbhmm::train( int id, std::map< int, std::vector<double *> > &sequences, int fz,
                  int nstates, int del )
{
  if( myhmms.count( id ) == 0 )
    myhmms[id] = new hmm;
  myhmms[id]->cleardata();
  myhmms[id]->clearmodel();
  for( std::map<int,std::vector<double *> >::iterator it = sequences.begin();
       it != sequences.end(); it++ )
    {
      myhmms[id]->adddata( it->second, fz );
      if(del == 1)
        for( unsigned i = 0; i < it->second.size(); i++ )
          delete [] it->second[i];
    }

  myhmms[id]->train( nstates );
  myhmms[id]->cleardata();
  savemodels( );
  return 0;
}

int dbhmm::prepthread( int numthreads, int tssize,
                       std::vector< thread_d > &thread_data)
{
  int hmmstates, statesize;
  for( std::map<int, hmm*>::iterator it = myhmms.begin();
       it != myhmms.end(); it++ )
    {
      it->second->prepthread();
      it->second->size( hmmstates, statesize );
    }
  for( int i = 0; i < numthreads; i++ )
    {
      thread_d t;
      t.calc_buffer = new long double[statesize];
      t.smatrix = new long double [tssize*hmmstates];
      thread_data.push_back( t );
    }
  return 0;
}

int dbhmm::cleanthreaddata( std::vector< thread_d > &thread_data )
{
  for( unsigned i = 0; i < thread_data.size(); i++ )
    {
      delete [] thread_data[i].calc_buffer;
      delete [] thread_data[i].smatrix;
    }
  return 0;
}

int dbhmm::classify_t( std::vector< matrix > &seq,
                       thread_d &thread_data )
{
  int sz;
  sz = seq.size();

  int maxid;
  double score;
  maxid = -1;
  score = 0;
  for( std::map< int, hmm* >::iterator it = myhmms.begin();
       it != myhmms.end(); it++ )
    {
      double cs;
      cs = it->second->match_t( seq, thread_data.smatrix,
                                thread_data.calc_buffer );
      if( maxid == -1 || cs > score )
        {
          maxid = it->first;
          score = cs;
        }
    }

  return maxid;
}

int dbhmm::classify( std::vector< double * > &seq, int fz )
{
  int sz;
  sz = seq.size();
  matrix *d;
  d = new matrix [sz];
  for( int i = 0; i < sz; i++ )
    d[i].init( seq[i], fz );
  int maxid;
  double score;
  maxid = -1;
  score = 0;
  for( std::map< int, hmm* >::iterator it = myhmms.begin();
       it != myhmms.end(); it++ )
    {
      double cs;
      cs = it->second->match( d, sz );
      if( maxid == -1 || cs > score )
        {
          maxid = it->first;
          score = cs;
        }
    }
  delete [] d;
  return maxid;
}

double dbhmm::scorediff( std::vector< double * > &seq, int fz,
                         int class1, int class2 )
{
  int sz;
  sz = seq.size();
  matrix *d;
  d = new matrix [sz];
  for( int i = 0; i < sz; i++ )
    d[i].init( seq[i], fz );
  double cs1, cs2;

  cs1 = myhmms[ class1 ]->match( d, sz );
  cs2 = myhmms[ class2 ]->match( d, sz );
  
  return cs1 - cs2;
}

int dbhmm::savemodels()
{
  std::vector< std::pair< int, hmm::hmmserialized* > > hmmdata;
  hmm::hmmserialized curms;
  databuffer buff;
  for( std::map< int, hmm* >::iterator it = myhmms.begin();
       it != myhmms.end(); it++ )
    {
      it->second->serialize( curms );
      buff.append( (char*)(&it->first), sizeof(int) );
      buff.append( (char*)(&(curms.size)), sizeof(int) );
      buff.append( curms.data, curms.size );
      delete [] curms.data;
    }
  buff.consolidate();
  classdb->modify_model_data( myid, buff.getptr(), buff.size() );
  return 0;
}

int dbhmm::loadmodels()
{
  char *data;

  int ret;
  classifierdb::model m;

  std::cout << "getting model\n";
  ret = classdb->get_model(myid, m );
  std::cout << "got data of size " << m.datasize << "\n";

  int curoff;
  curoff = 0;
  while( curoff < m.datasize )
    {
      int tid, tsize;
      memcpy( &tid, m.data+curoff, sizeof(int) );
      memcpy( &tsize, m.data+curoff+sizeof(int), sizeof(int) );
      curoff = curoff + 2*sizeof(int);
      
      myhmms[tid] = new hmm;
      hmm::hmmserialized hmmdata;
      hmmdata.data = m.data+curoff;
      hmmdata.size = tsize;
      myhmms[tid]->deserialize( hmmdata );
      curoff = curoff + tsize;
      std::cout << "loaded model " << tid << "\n";
    }
  std::cout << "loaded total of " << myhmms.size() << "\n";
  std::cout << "model created on " << m.createdate << "\n";
  delete [] m.data;
  return 0;
}
