#ifndef __IMAGE2D__
#define __IMAGE2D__

#ifndef _NOMAGICK_
#include <Magick++.h>
#endif

#include <iostream>
#include <string>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <fftw3.h>
#include "complex.h"
#include "subimage.h"

#define CH_RED 1
#define CH_GREEN 2
#define CH_BLUE 3
#define CH_GRAY 4
// lum - saturation
#define CH_VS 5

#ifndef PI
#define PI 3.14159265358979323846
#endif

/*

+-----------------------------> +y
|
|
|
|
|
|
|
|
|
|
|
V
+x
*/


///class to contain a 2d image
class image2d
{
 public:
  image2d();
  image2d( const image2d &in );

  ~image2d();

  /// copy
  image2d &operator=(const image2d &in);

  ///delete the current image
  int del();

  ///clears an image
  int clear();

  ///create a blank image;
  int create(int h, int w);

  int copysubim( double *oim, int lx, int ly,
                 int h, int w, int ph, int pw );

  ///load a lower resolution of an image
  int loadlowres( image2d *im, int ratio );

#ifndef _NOMAGICK_
  ///load an image
  int load(Magick::Image &i, int channel);
#endif

  ///copy an image from another image
  int load(image2d *im);

  ///loads a image from complex array data set
  int load(Complex *data, int height, int width);

  int load(double *data, int h, int w);
  int load( int *data, int h, int w);
  int loadcopy(double *data, int h, int w);
  int load(subimage *im);

  /// increase the size of the image padding with zeros
  /**
   * expand image from top left corner
   */
  int padzeros( int nszh, int nszw );

  /// crops the image to new size
  int crop( int nszh, int nszw );

#ifndef _NOMAGICK_
  ///copys the current image back to magick
  int getimage(Magick::Image &i);

  ///copys the current image bounded by box back to magick
  int getimage(Magick::Image &i, int minx, int miny,
               int maxx, int maxy);

  ///copys a normalized copy of the image
  int getimage_normalized(Magick::Image &i);
#endif

  ///get a pixel or the closest pixel
  double getpixel(int x, int y);

  /// converts the image into short ints stored in array d
  int getshortint( int *d );

  /// get pixel using bilinear interpolation
  double getpixelb( double x, double y );

  ///get a pixel using normalized coor
  /**
   * take weighted average of the 4 nearest pixels
   */
  double getpixeld( double x, double y );

  ///set a pixel
  int setpixel(int x, int y, double value);

  ///copys the image to location in memory
  int getimage(double *ptr);

  ///returns the pointer to the image
  double *getimageptr()
    {
      return image;
    }

  ///normalizes the image so min and max fall between 0,1
  int normalizeminmax();

  int normalize( double min, double max );

  ///get width
  int getwidth()
    {
      return width;
    }

  ///get height
  int getheight()
    {
      return height;
    }

  ///adds pixels from image with binary image
  int add(image2d *im);

  int sub( image2d *im );

  int add(subimage *im);

  /// adds an value to the image
  int add(double v);

  /// multiply value to image 
  int multiply( double v );

  /// returns the max value
  double min();
  /// returns the min value
  double max();

  int edgedetect( image2d *im );

  ///check if image created
  int valid()
    {
      if(image == NULL)
        return 0;
      return 1;
    }

  int gamma( double g );

  /// calculate difference of gaussians
  int dog( double sigma1, double sigma2 );

  /// calculate the gaussian of the image
  int gaussian(double sigma);

  /// calculates gaussian blurs for multiple sigmas
  int gaussian( double *sigmas, image2d **outim, int num );

  /// median filter on image
  /**
   * @param fsize size of the filter
   */
  int median( int fsize );

  /// quantize my multiplying by a factor
  /**
   * will cap the results so it is smaller than factor
   * image will be all integers
   */
  int multquantize( int factor );

  /// divide
  int dividefactor( int factor );

  int divide( double factor );

  /// calculates the average pixel value
  double average();

  double stdvar();

  /// substracts an amount from all pixels
  /**
   * clips the values at 0
   */
  int subtract_lowerbound(double cut);

  ///calculates the varance
  /**
   * @param avg the calculated average
   */
  double variance(double avg);

  /// puts the image in log scale
  int logscale();

  /// log scale with multiplication factor
  int logscale( double s );

  /// log a normalized image [0 1]
  int logmult( double s );

  /// return the mutual information between two images
  /**
   * both images must be of same size
   */
  double mutualinformation( image2d *o, int minx, int maxx, 
                            int miny, int maxy);

  /// logical and with another image
  int logicaland( subimage &a, subimage &b );
  int logicaland( image2d &a );

  /// sets all values to
  int setall( double v );

  int checkint();

  double sum();
 
 private:

  int getpixelbox(int cx, int cy, int r, double *values);

  int createdog( double *g, double sigma1,
                 double sigma2, int szx, int szy );

  int jointpmfblur( double a[64][64], double b[64][64] );
  int pmfblur( double a[64], double b[64] );

  int convfilter(double *filter, int x, int y);

  /// convolute with a filter in frequency domain
  int convolute( fftw_complex *F );

  int convolute( double *F );

  /// create a gaussian filter
  int creategauss( double *g, double sigma1,
                   int pszx, int pszy );

  /// create gaussian filter in frequency domain
  int creategaussf( double *g, double sigma1, int szx, int szy );

  int print( fftw_complex *d, int height, int width, double sigma );

  double *image;
  int width;
  int height;
};

#endif
