#ifndef __ALIGNIMAGE_H__
#define __ALIGNIMAGE_H__

#include "image2d.h"
#include "dbimage.h"
#include "transformation.h"
#include "dbalnimage.h"
#include "tile.h"
#include "tileimage.h"
#include "pthread.h"

struct alignimage_blend_td
{
  pthread_t tid;
  std::vector<pthread_mutex_t *> lowlock;
  std::vector<pthread_mutex_t *> highlock;
  pthread_barrier_t *sync;
  pthread_mutex_t *clock;
  image2d *overlap;
  std::vector< image2d* > *cmaps;
  std::vector< double* > dptrs;   ///< data pointers to raw cmap data
  int imh;                        ///< height of image
  int imw;                        ///< width of image
  int highrow;                    ///< end row
  int lowrow;                     ///< start row
  std::vector< std::pair< int, int > > *todo;
  std::vector< int > lowlockidx;  ///< index to release the low lock
  std::vector< int > highlockidx; ///< index to acquire the high lock
  int sz;
};

double blend_calc( int x, int y, double *d, int imh, int imw );
void *blendthread_start( void *);
int blend_calctodo( alignimage_blend_td *atd );
int blend_cleanup( alignimage_blend_td *atd );
int blend_mainloop( alignimage_blend_td *atd );
double blend_calc( int x, int y, double *d, int imh, int imw );

/// an set of semi aligned images
class alignimage
{
 public:
  alignimage();
  ~alignimage();
  struct transrange
  {
    double lowtx; ///< low and high tranformation bounds
    double lowty;
    double hightx;
    double highty;
  };
  struct setconn
  {
    int aid;
    transrange con;  ///< transformation to apply to aid
  };
  /// defines a connection between two image sets
  struct setconn2
  {
    int a, b;
    transrange con;  ///< transformation that takes a to b
  };
  struct tileinfo
  {
    int x, y;
    double transx, transy;
    bool operator() ( const tileinfo &a, const tileinfo &b ) const
    {
      if( a.x < b.x )
        return -1;
      else if( a.x > b.x )
        return 1;
      else
        {
          if( a.y < b.y )
            return -1;
          else if( a.y > b.y )
            return 1;
          return 0;
        }
    }
  };
  struct alignedset
  {
    int aid;         ///< the alignment id correspoinding to the one in the database
    int sizex;       ///< image size x
    int sizey;       ///< image size y
    std::set< tileinfo, tileinfo > tiles;             ///< base tiles this image contains
    std::vector< setconn > connections;     ///< connections to other align set
  };

  /// defines search space for aligning image aid1 to image aid2
  struct alignjob
  {
    int aid1;
    int aid2;
    int lowx;
    int lowy;
    int highx;
    int highy;
  };

  int init(  dbalnimage *in );

  /// delete the datastructure
  int del();

  /// load data structure from database
  int load();

  /// create a set of aligned images
  /**
   * @param tile list of tiles to create from
   * @param conn list of connections describing how the tiles connect
   */
  int createsets( std::vector< tile* > &tile,
                  std::vector< setconn2 > &conn );

  int mergesets( std::vector< tile* > &tile, 
                 std::vector< setconn2 > &conn,
                 int iteration );

  int getrowalnjobs( std::vector< alignjob > &joblist, int iteration );

  int dotout();

  int deliteration( int iter );
  int delaid( int daid );

  int showbox( int aid, Magick::Image &im, dbimage *imdb, Magick::Image &noblend );

 private:
  
  struct imageset
  {
    int level;  ///< level the alignment of images is at
    std::map<int, alignedset* > sets; ///< image sets at this level key the set id (aid)
  };

  /// routine to color segments of the graph
  int colorsegments( std::vector<int> &flag, std::vector<int> &degree,
                     std::map< std::pair<int, int>, transrange > &edge );

  int caltransforms( int start,
                     std::vector< tile* > &tiles,
                     std::map< std::pair<int, int>, transrange > &edges,
                     std::vector<std::pair<tile*, transrange> > &ts );

  int createalignset( int iteration,
                      std::vector< std::pair<tile*, transrange> > &ts );

  int addtoimageset( int aid,
                     std::vector< std::pair<tile*, transrange> > &ts,
                     int &imh, int &imw);

  int createsetlinks( int iteration );

  int createlinks( int iteration, int aid1,
                   int aid2 );

  int addconnection( alignedset *aset, int aid2, transrange tr );

  int createimage( int aid, int slice, int channel,
                   std::vector< std::pair<tile*, transrange> > &ts,
                   std::vector< image2d* > &imcontribute );

  int addimage( image2d *in, image2d *contribute, transrange p,
                image2d &im );

  int addtile( int curnode,
               std::vector< tile* > &tiles,
               std::map< std::pair<int, int>, transrange > &edges,
               std::vector< std::pair<tile*, transrange> > &ts,
               transrange basetrans);

  int graphdfs( std::vector<int> &flag,
                std::map< std::pair<int, int>, transrange > &edge,
                int i, int color );

  int addimag_calcblend( std::vector< image2d* > cmaps, 
                         image2d &immap, image2d &imoverlap );

  int calcblending( int x, int y, std::vector< image2d* > &cmaps,
                    image2d &immap, int h, int w,
                    std::vector< double > &blending );

  int addimage_mark( image2d &origmap, image2d &immark, image2d &immap,
                     image2d &imoverlap, transrange p,
                     int imh, int imw);

  int fixtransforms( std::vector< std::pair<tile*, transrange> > &ts,
                     std::pair<int, double> &maxtx,
                     std::pair<int, double> &maxty );

  int preparemerge( std::vector< std::pair<tile*, transrange> > &ts,
                    image2d &im, image2d &imoverlap,
                    std::pair<int, double> &maxtx,
                    std::pair<int, double> &maxty,
                    std::vector< image2d* > &imcontribute );

  int savelinks( int iteration );

  std::vector< imageset* > imsets;
  dbalnimage *db;
};

#endif
