#include "GLCPimage.h"

void *pt_GLCPimstart( void *arg );
int GLCPimstart( GLCPimage::thread_d *mytd );

void *pt_GLCPimstart( void *arg )
{
  GLCPimage::thread_d *mytd;
  mytd = ( GLCPimage::thread_d* )arg;
  std::cout << "thread " << (unsigned long int)(mytd->tid) << " started\n";
  GLCPimstart( mytd );
  return NULL;
}

int GLCPimstart( GLCPimage::thread_d *mytd )
{
  GLCP ss;
  std::vector< matrix > myts;
  int h, w;
  h = mytd->im->getheight();
  w = mytd->im->getwidth();
  int sz;
  int mf;
  std::cout << "thread " << mytd->start << " starting " << h << " " 
            << w << " step " << mytd->step << " mod " << mytd->mod << "\n";
  int skip;
  skip = 0;
  for( int z = mytd->start; z < h*w; z = z + mytd->mod*mytd->step )
    {
      int i, j;
      i = z / w;
      j = z % w;
      myts.clear();
      if( mytd->immask == NULL )
        mf = 1;
      else if( mytd->immask->getpixel( i, j ) > 0.5 )
        mf = 1;
      else
        {
          mf = 0;
          skip++;
        }
      if( mf == 1 )
        {
          if( ss.init( mytd->im, i, j, 14 ) == 0 )
            {
              ss.getfeaturets( myts, mytd->mymask, sz );
              if( myts[0].rows() != 7 )
                {
                  std::cout << "ERROR fj;klafkldns\n";
                  std::cout << mytd->mymask.entropy << "\n"
                            << mytd->mymask.contrast << "\n"
                            << mytd->mymask.correlation << "\n"
                            << mytd->mymask.power << "\n"
                            << mytd->mymask.mean << "\n";
                  std::cout << "\n\n" << myts[0].rows() << " "
                            << myts[0].cols() << "\n";
                  exit(0);
                }
              int c;
              c = mytd->curhmmdb->classify_t( myts, mytd->tbd );
              if( c == 0 )
                mytd->classmap->setpixel(i, j, -1);
              else
                mytd->classmap->setpixel(i, j, 1);
            }
          else
            mytd->classmap->setpixel(i, j, -1);
        }
      else
        {
          mytd->classmap->setpixel(i, j, -1);
        }
    }
  std::cout << "skipped " << skip << "\n";
  return 0;
}

GLCPimage::GLCPimage()
{
}

GLCPimage::~GLCPimage()
{
}

int GLCPimage::classify_vote( int radius )
{
  image2d nim;
  int h, w;
  h = classmap.getheight();
  w = classmap.getwidth();
  std::cout << "voteing " << h << " " << w << "\n";
  nim.create( h, w );
  nim.clear();
  for( int i = 0; i < h; i++ )
    {
      for( int j = 0; j < w; j++ )
        {
          int li, lj, hi, hj;
          li = i - radius;
          lj = j - radius;
          hi = i + radius;
          hj = j + radius;
          if( li >= 0 && lj > 0 && hi < h && hj < w )
            {
              int tp, tn;
              tp = tn = 0;
              for( int x = li; x < hi; x++ )
                {
                  for( int y = lj; y < hj; y++ )
                    {
                      double z;
                      z = sqrt( pow( x - i, 2) + pow( y - j, 2 ) );
                      if( z < (double) radius )
                        {
                          double c;
                          c = classmap.getpixel( x, y );
                          if( c > 0.5 )
                            tp = tp + 1;
                          else if( c < -0.5 )
                            tn = tn + 1;
                        }
                    }
                }
              if( tp > tn )
                {
                  nim.setpixel( i, j, 1 );
                }
              else
                {
                  nim.setpixel( i, j, -1 );
                }
            }
        }
    }
  classmap.load( &nim );
  return 0;
}

int GLCPimage::load( Magick::Image &mim )
{
  int h, w;
  Magick::Geometry geo = mim.size();
  w = geo.width();
  h = geo.height();
  classmap.create( h, w );
  std::cout << "loading classmap " << h << " " << w << "\n";
  Magick::ColorRGB pix;
  Magick::PixelPacket *pc = mim.getPixels(0, 0, w, h);
  for( int i = 0; i < h; i++ )
    {
      for( int j = 0; j < w; j++ )
        {
          pix = pc[i*w+j];
          if( pix.red() > pix.blue() )
            classmap.setpixel( i, j, 1 );
          else
            classmap.setpixel( i, j, -1 );
        }
    }
  return 0;
}

int GLCPimage::overlay( Magick::Image &mim )
{
  int h, w;
  h = classmap.getheight();
  w = classmap.getwidth();
  std::cout << "GLCP overlay size " << h << " " << w << "\n";
  Magick::ColorRGB red( "red" );
  Magick::ColorRGB blue( "blue" );
  mim.type( Magick::TrueColorType );
  Magick::PixelPacket *pixel_cache = mim.getPixels(0,0,w,h);

  for( int i = 0; i < h; i++ )
    {
      for( int j = 0; j < w; j++ )
        {
          double value;
          value = classmap.getpixel(i,j);

          if( value > 0.5 )
            pixel_cache[i*w+j] = red;
          else if( value < -0.5 )
            pixel_cache[i*w+j] = blue;
        }
    }

  mim.syncPixels(); 
  return 0;
}

int GLCPimage::classifyimage( image2d *im, dbhmm *mydb,
                              GLCP::mask &mymask, int step,
                              image2d *immask)
{
  classmap.create( im->getheight(), im->getwidth() );
  class1score.create( im->getheight(), im->getwidth() );
  class2score.create( im->getheight(), im->getwidth() );

  std::cout << "images initialized\n";
  std::cout << "selected metrics\n";
  if( mymask.mean == 1 )
    std::cout << "mean\n";

  if( mymask.entropy == 1 )
    std::cout << "entropy\n";

  if( mymask.contrast == 1 )
    std::cout << "contrast\n";

  if( mymask.correlation == 1 )
    std::cout << "correlation\n";
  if( mymask.power == 1 )
    std::cout << "power\n";
  std::cout << "init " << NUMTHREADS << " threads\n";
  curim = im;
  curhmmdb = mydb;
  std::vector<dbhmm::thread_d> tbd;
  std::vector< thread_d > td;
  mydb->prepthread( NUMTHREADS, 30, tbd );
  std::cout << "db preped\n";
  for( int i = 0; i < NUMTHREADS; i++ )
    {
      thread_d mycd;
      mycd.im = curim;
      mycd.curhmmdb = curhmmdb;
      mycd.start = i*step;
      mycd.mod = NUMTHREADS;
      mycd.step = step;
      mycd.classmap = &classmap;
      mycd.class1score = &class1score;
      mycd.class2score = &class2score;
      mycd.mymask = mymask;
      mycd.immask = immask;
      mycd.tbd = tbd[i];
      td.push_back( mycd );
    }
  for( int i = 0; i < NUMTHREADS; i++ )
    {
      std::cout << "starting thread " << i << "\n";
      pthread_create( &(td[i].tid), NULL, pt_GLCPimstart, &(td[i]) );
      std::cout << "started\n";
    }
  std::cout << "threads startec\n";
  void *ret;
  for( int i = 0; i < NUMTHREADS; i++ )
    {
      pthread_join( td[i].tid, &ret );
    }
  mydb->cleanthreaddata( tbd );

  return 0;
}

int GLCPimage::seperate_histogram( Magick::Image &mim )
{
  int h, w;
  h = classmap.getheight();
  w = classmap.getwidth();
  Magick::ColorGray shade( 0 );
  Magick::PixelPacket *pixel_cache = mim.getPixels(0,0,w,h);

  for( int i = 0; i < h; i++ )
    {
      for( int j = 0; j < w; j++ )
        {
          double value;
          value = classmap.getpixel(i,j);

          if( value > 0 )
            {
              shade = pixel_cache[i*w+j];
              value = shade.shade();
              shade.shade( value / 2.0 + 0.5 );
              pixel_cache[i*w+j] = shade;
            }
          else
            {
              shade = pixel_cache[i*w+j];
              value = shade.shade();
              shade.shade( value / 2.0 );
              pixel_cache[i*w+j] = shade;
            }
        }
    }

  mim.syncPixels(); 
  return 0;
}
