#include "floatfann.h"

#include <ios>
#include <iostream>
#include <iomanip>
#include <fstream>

#include "GLCP.h"
#include "dbimage.h"
#include "bdbenv.h"
#include "dbtmpfeature.h"
#include <tclap/CmdLine.h>
#include <string>
#include "classifierdb.h"

struct fann_data_pair
{
  fann_type *input;
  fann_type *output;
};

dbtmpfeature *DBGOOD, *DBBAD;
int numgood, numbad;
GLCP::mask GLCPmask;

void fann_data_callback( unsigned int num, unsigned int insz, 
                         unsigned int outsz, fann_type *in, fann_type *out)
{
  int fsz = 0;
 
  int bsz;
  DBGOOD->getrecsize( bsz );

  char *buff;
  buff = new char [bsz];
  GLCP myglcp;

  if( num >= numbad )
    {
      out[0] = 1;
      if( num - numbad > numgood )
        {
          std::cout << "ERROR good num id\n";
          exit(0);
        }
      DBGOOD->get( num-numbad+1, buff );
    }
  else
    {
      out[0] = -1;
      DBBAD->get( num+1, buff );
    }

  myglcp.del();
  myglcp.deserialize( buff, bsz );
  int nf;
  std::vector< double > s;
  myglcp.meanthetadelta( GLCPmask, s );

  for( int j = 0; j < s.size(); j++ )
    {
      in[j] = s[j];
      //      std::cout << in[j] << " ";
    }
  //  std::cout << "\n";
  //  std::cout << out[0] << "\n";
  delete [] buff;
}



int shuffle( std::vector< fann_data_pair > &l )
{
  std::vector< fann_data_pair > newlist;
  newlist.resize( l.size() );
  int flag = 0;
  int counter = 0;
  while( flag < 5 )
    {
      int s;
      s = random()%l.size();
      if( l[s].input == NULL )
        flag++;
      else
        {
          newlist[counter] = l[s];
          counter++;
          l[s].input = NULL;
        }
    }
  for(unsigned s = 0; s < l.size(); s++ )
    {
      if( l[s].input != NULL )
        {
          newlist[counter] = l[s];
          counter++;          
        }
    }
  if( counter != l.size() )
    {
      std::cout << "shuffle error\n";
      exit(0);
    }
  l = newlist;
  return 0;
}

int checksamples( dbtmpfeature *dbtmp, double out,
                  GLCP::mask mymask, fann *net )
{
  int ret = 0;
  unsigned int maxid;
  maxid = dbtmp->maxid();
  int fsz = 0;
 
  fsz = mymask.entropy + mymask.contrast +
    mymask.correlation + mymask.power + mymask.mean;

  int bsz;
  dbtmp->getrecsize( bsz );

  char *buff;
  buff = new char [bsz];
  GLCP myglcp;

  int numfeatures = 0;

  fann_type *annin, *annout;
  for( unsigned i = 1; i < maxid; i++ )
    {
      dbtmp->get( i, buff );
      std::vector< double > s;
      s.clear();
      myglcp.del();
      myglcp.deserialize( buff, bsz );
      int nf;
      myglcp.meanthetadelta( mymask, s );
      nf = (int)s.size();
      
      if( i > 1 && nf != numfeatures )
        {
          std::cout << "ERROR number of features inconsistant\n";
          exit(0);
        }
      else
        annin = new fann_type[nf];
      
      numfeatures = nf;
      for( int j = 0; j < numfeatures; j++ )
        {
          annin[j] = s[j];
        }
      annout = fann_run( net, annin );
      float z;
      z = annout[0];
      if( ( z > 0.0 &&  out > 0.0 ) 
          || ( z < 0.0 &&  out < 0.0 ) )
        ret++;
      else if( i % 1000 == 0 )
        {
          for( int j = 0; j < numfeatures; j++ )
            {
              std::cout << annin[j] << " ";
            }
          std::cout << " expect " << out << " got " << z << "\n";
        }
    }
  delete [] annin;
  std::cout << "ERROR " << ret << "/" << maxid << "\n";
  return ret;
}

int outputsamples( dbtmpfeature *dbtmp, double out,
                   GLCP::mask mymask, std::vector< fann_data_pair > &list,
                   int step )
{
  unsigned int maxid;
  maxid = dbtmp->maxid();
  int fsz = 0;
 
  fsz = mymask.entropy + mymask.contrast +
    mymask.correlation + mymask.power + mymask.mean;

  int bsz;
  dbtmp->getrecsize( bsz );

  int numsamples = (maxid - 1 )/step;

  char *buff;
  buff = new char [bsz];
  GLCP myglcp;
  int counter;
  int numfeatures = 0;
  counter = 0;

  for( unsigned i = 1; i < maxid; i = i + step )
    {
      dbtmp->get( i, buff );
      std::vector< double > s;
      s.clear();
      myglcp.del();
      myglcp.deserialize( buff, bsz );
      int nf;
      myglcp.meanthetadelta( mymask, s );
      nf = (int)s.size();
      
      if( i > 1 && nf != numfeatures )
        {
          std::cout << "ERROR number of features inconsistant\n";
          exit(0);
        }
      numfeatures = nf;

      fann_data_pair fdp;
      fdp.input = new fann_type[numfeatures];
      fdp.output = new fann_type[1];
      fdp.output[0] = out;
      for( int j = 0; j < numfeatures; j++ )
        fdp.input[j] = s[j];
      list.push_back( fdp );
      counter++;
    }
  std::cout << "loaded " << counter << " elements\n";
  return 0;
}

int main( int argc, char *argv[] )
{

#ifndef _NOMAGICK_
  try
    {
      ///set up the command line parsing
      TCLAP::CmdLine cmd("Image alignment program", ' ', "0.001");

      TCLAP::ValueArg<std::string> dbdir("d", "db", "database dirctory", true,
                                         "", "databaase");
      TCLAP::ValueArg<std::string> dbgood("g", "goodtmpdb",
                                          "good tmp database dirctory", true,
                                          "", "file");

      TCLAP::ValueArg<std::string> dbbad("b", "badtmpdb",
                                         "good tmp database dirctory", true,
                                         "", "file");
      
      TCLAP::ValueArg< bool > fmean("", "mean", "mean flag", false,
                                    true, "true/false" );
      TCLAP::ValueArg< bool > fentropy("", "entropy", "entropy flag", false,
                                       true, "true/false" );
      TCLAP::ValueArg< bool > fcontrast("", "contrast", "contrast flag", false,
                                        true, "true/false" );
      TCLAP::ValueArg< bool > fcorrelation("", "correlation", "correlation flag",
                                           false, true, "true/false" );
      TCLAP::ValueArg< bool > fpower("", "power", "power flag", false,
                                     true, "true/false" );
      TCLAP::ValueArg< bool > tsaverage( "", "average", "average deltas", false,
                                         false, "true/false");

      cmd.add(dbdir);

      cmd.add( dbgood );
      cmd.add( dbbad );

      cmd.add( fmean );
      cmd.add( fentropy );
      cmd.add( fcontrast );
      cmd.add( fcorrelation );
      cmd.add( fpower );
      cmd.add( tsaverage );

      bdbenv bdb;
      dbtmpfeature dbtf_g, dbtf_b;

      cmd.parse(argc, argv);

      dbtf_b.setname( dbbad.getValue() );
      dbtf_g.setname( dbgood.getValue() );

      bdb.open( dbdir.getValue() );

      dbtf_b.init( bdb.envptr(), 0 );
      dbtf_g.init( bdb.envptr(), 0 );

      bdb.adddb( &dbtf_b );
      bdb.adddb( &dbtf_g );
      bdb.cleanlog();
      
      GLCP::mask mymask;
      if( fmean.getValue() == true )
        mymask.mean = 1;
      else
        mymask.mean = 0;

      if( fentropy.getValue() == true )
        mymask.entropy = 1;
      else
        mymask.entropy = 0;

      if( fcontrast.getValue() == true )
        mymask.contrast = 1;
      else
        mymask.contrast = 0;

      if( fcorrelation.getValue() == true )
        mymask.correlation = 1;
      else
        mymask.correlation = 0;

      if( fpower.getValue() == true )
        mymask.power = 1;
      else
        mymask.power = 0;

      GLCPmask = mymask;
      numgood = dbtf_g.maxid();
      numbad = dbtf_b.maxid();
      DBGOOD = &dbtf_g;
      DBBAD = &dbtf_b;

      std::cout << "data load complete training...\n";

      fann *net;
      int fsz;
      fsz = mymask.entropy + mymask.contrast +
        mymask.correlation + mymask.power + mymask.mean;
      std::cout << "nn size " << fsz << "\n";
      net = fann_create_standard(3, fsz, fsz*2, fsz*0.5, 1);
      
      fann_set_activation_function_hidden(net, FANN_SIGMOID_SYMMETRIC);
      fann_set_activation_function_output(net, FANN_SIGMOID_SYMMETRIC);
      fann_randomize_weights( net, -1, 1 );
      fann_train_data *ftd;
      ftd = fann_create_train_from_callback( numgood+numbad, fsz, 1, fann_data_callback);
      fann_shuffle_train_data( ftd );
      //fann_cascadetrain_on_data( net, ftd, fsz, 2, 0.01 );
      fann_train_on_data( net, ftd, 1000, 2, 0.01 );
      fann_destroy_train( ftd );
      std::cout << "training done\n";
      
      std::cout << "check good\n";
      checksamples( &dbtf_g, 1.0, mymask, net );
      std::cout << "check bad\n";
      checksamples( &dbtf_b, -1.0, mymask, net );

      fann_save( net, "myann.conf" );

      dbtf_b.close();
      dbtf_g.close();
      bdb.close();

      fann_destroy( net );
      ///////////////////////////////////////////////
    }
  catch (TCLAP::ArgException &e)
    {
      std::cout << "ERROR: " << e.error() 
                << " for arg " << e.argId() << "\n";
      return 0;
    }
  
#else
  std::cout << "compiled with no image magick\n";
#endif
  return 0;
}
