#ifndef _IMAGE_H
#define _IMAGE_H
#include<stdio.h>
#include<math.h>
#include<Stuff\Stuff.hpp>
#include<windows.h>


//>>>>>>>>>>>>>>>>>>>>>>>>> Error Handleing Cluge
//!!!!!!!!!!!!!!!!!!!!!!!!! This must be addressed
#define GENFERR(x) PAUSE((x))
#define GENNFERR(x) PAUSE((x))


//>>>>>>>>>>>>>>>>>>>>>>>>>>> Errors
#define IERR_OK	     (1<<0)
#define IERR_FILE	 (1<<1)
#define IERR_NOIMAGE (1<<2)

//>>>>>>>>>>>>>>>>>>>>>>>>>>>> Flags
#define IMG_LOCKED	 (1<<0)
#define IMG_ISDATA	 (1<<1)



//>>>>>>>>>>>>>>>>>>>>>>>>>>>> Macros
#define RGB16(r,g,b)  ((((WORD)r&0xf8)<<8)|(((WORD)g&0xfc)<<3)|(((WORD)b&0xf8)>>3))                        
#define RGB24(r,g,b,a,mr,mg,mb,ma) (PMASK24(r,mr)|PMASK24(g,mg)|PMASK24(b,mb)|PMASK24(a,ma))
#define PMASK24(p,mp) ((((DWORD)(p))|((DWORD)(p)<<8)|((DWORD)(p)<<16)|((DWORD)(p)<<24))&(mp))
#define Mask(x,y) ((x)&(~(y)))

//>>>>>>>>>>>>>>>>>>>>>>>>>>>  Structs/unions

enum  ITYPE {ITYPE_RGB,ITYPE_INDEXED} ;	
enum  RMETHOD {RMD_REDC,RMD_BLUEC,RMD_GREENC,RMD_ALPHAC,RMD_MAX,RMD_BHT} ;

//>>>>>>>>>>>>>>>>>>>>>>>>>>>  custom types
typedef unsigned long int DWORD;
typedef unsigned short int WORD;
typedef unsigned char BYTE;
typedef int BOOL;

#define TRUE  1
#define FALSE 0

//>>>>>>>>>>>>>>>>>>>>>>>>>>>  Classes
class Image;
class RGBMask;
class ImgRGBColor;
class RGBPixel;

class FRect
{
public:
	float top,left,right,bottom;
	FRect(float x1=0,float y1=0,float x2=0,float y2=0)   
	{top=y1; bottom=y2; left=x1; right=x2; }
	FRect(double x1,double y1=0,double x2=0,double y2=0) 
	{top=(float)y1; bottom=(float)y2; left=(float)x1; right=(float)x2; }
	FRect(int x1,int y1,int x2,int y2) 
	{top=(float)y1; bottom=(float)y2; left=(float)x1; right=(float)x2; }
};


class TRect:public RECT
{
public:
	TRect(int x1=0,int y1=0,int x2=0,int y2=0) {top=y1; bottom=y2; left=x1; right=x2; }
	TRect(FRect &rct) 
	{top=(int)rct.top; bottom=(int)rct.bottom;
	left=(int)rct.left; right=(int)rct.right; }
};

class RGBPixel
{
public:
	
	union {
		unsigned long  int dword;
		unsigned short int wrd[2];
		unsigned char      com[4];
	}; 
	
	RGBPixel(const ImgRGBColor &col,const RGBMask &mask) { Set(col,mask); }
	void Set(const ImgRGBColor &col,const RGBMask &mask);
	void Set(const ImgRGBColor &col);
	friend ImgRGBColor;
};


class RGBMask 
{
protected:
	DWORD R,G,B,A; 
	DWORD irs,igs,ibs,ias; 
	DWORD rbs,gbs,bbs,albs; 
	DWORD ras,gas,bas,aas; 
	DWORD bits;	
	// DWORD rmax,gmax,bmax,amax; 
public:
	RGBMask(DWORD r=0xff0000,DWORD g=0x00ff00,DWORD b=0x0000ff,DWORD a=0) {  Set(r,g,b,a); }
	int Set(DWORD r=0xff0000,DWORD g=0x00ff00,DWORD b=0x0000ff,DWORD a=0);
	int IsValid()  { return R && B && G; }
	int operator==(RGBMask &mask) { return (R==mask.R)&&(G==mask.G)&&(B==mask.B)&&(A==mask.A); }
	DWORD GetRMask() {return R;}
	DWORD GetGMask() {return G;}
	DWORD GetBMask() {return B;}
	DWORD GetAMask() {return A;}
	DWORD GetRBits() {return rbs;}
	DWORD GetGBits() {return gbs;}
	DWORD GetBBits() {return bbs;}
	DWORD GetABits() {return albs;}
	DWORD GetBpp()   {return bits;}
	friend class ImgRGBColor;
	friend class RGBPixel;
};

class ImgRGBColor 
{
private:
	
public:
	int   r,g,b,a; 
	void Bound();	   //makes sure color is in valid range
	//construction
	ImgRGBColor() {}
	ImgRGBColor(int   ir,int ig,int ib,int ia=255)  { Set(ir,ig,ib,ia);}
	ImgRGBColor(float ir,float  ig,float ib,float ia=1.0) { Set(ir,ig,ib,ia);}
	ImgRGBColor(DWORD ir,DWORD  ig,DWORD ib,DWORD ia=255) { Set(ir,ig,ib,ia);}
	ImgRGBColor(RGBPixel &pix,RGBMask &mask)				{ Set(pix,mask);   }
	ImgRGBColor(COLORREF col)				{ Set(col&0xff,(col>>8)&0xff,(col>>16)&0xff);   }
	
	//initalization
	void Set(DWORD ir,DWORD ig,DWORD ib,DWORD ia=255)	{ r=ir;  g=ig; b=ib; a=ia; }
	void Set(int ir,int ig,int ib,int ia=255)			{ r=ir;  g=ig; b=ib; a=ia; }
	void Set(float ir,float ig,float ib,float ia=1.0)	{ Set((int)(ir*255),(int)(ig*255),(int)(ib*255),(int)(ia*255));}
	void SetBounded(int ir,int ig,int ib,int ia=255)   { Set(ir,ig,ib,ia); Bound(); }	 //keeps values in legal range
	void Set(const RGBPixel &pix,const RGBMask &mask);				 
	
	//conditionals
	int operator==(const ImgRGBColor &ac)	{ return (r==ac.r) && (g==ac.g) && (b==ac.b); }
	int operator!=(const ImgRGBColor &ac)   { return (r!=ac.r) || (g!=ac.g) || (b!=ac.b); }
	int LSDiff(const ImgRGBColor &ac)		{ return abs(ac.r-r)+abs(ac.g-g)+abs(ac.b-b)+abs(ac.a-a); }		//least squares difference
	void Merge(const ImgRGBColor &ac,int dw=1,int sw=1);	//weighted merge
	void Blend(const ImgRGBColor &ac,float trans=.5);	//weighted merge
	void ContrastBy(float cont) { SetBounded((int)((r-128*cont)+128),(int)((g-128*cont)+128),(int)((b-128*cont)+128)); }
	void BrightenBy(float bgt) { SetBounded((int)(r*bgt),(int)(g*bgt),(int)(b*bgt));  }
	
	//operators
	ImgRGBColor &operator-(int col)	{ Set(r-col,g-col,b-col,a);   return *this; }
	ImgRGBColor &operator-(float col) { Set(r-col*255,g-col*255,b-col*255,(float)(a/255.0)); return *this; }
	ImgRGBColor &operator+(int col)   { Set(r+col,g+col,b+col,a);   return *this; }
	ImgRGBColor &operator+(float col) { Set(r+col*255,g+col*255,b+col*255,(float)(a/255.0)); return *this; }
	ImgRGBColor &operator=(int col)	{ Set(col,col,col); return *this;}
	ImgRGBColor &operator=(float col) { Set(col,col,col); return *this;}
	ImgRGBColor &operator++()			{ Set(r+1,g+1,b+1,a); return *this; }
	ImgRGBColor &operator--()			{ Set(r-1,g-1,b-1,a); return *this; }
	ImgRGBColor &operator+=(const ImgRGBColor &ac) { Set(r+ac.r,g+ac.g,b+ac.b);   return *this; } 
	ImgRGBColor operator+(const ImgRGBColor &ac) { return ImgRGBColor(r+ac.r,g+ac.g,b+ac.b); }
	ImgRGBColor operator/(int dv)		  { return ImgRGBColor(r/dv,g/dv,b/dv); }
	ImgRGBColor &operator=(const ImgRGBColor &ac) { r=ac.r; g=ac.g; b=ac.b; a=ac.a; return *this; }
	
	//operations
	void Invert()  { r=255-r; g=255-g; b=255-b; a=255-a; }
	void MakeBW()  { r=g=b=(BYTE)(g*0.59f+b*0.11f+r*0.3f); }
	float GetBrightness() { return (g*0.59f+b*0.11f+r*0.3f)*(1/255.0f);}
	
	friend class RGBPixel;
	friend class Image;
	friend class DXImage;
	friend class OctTree;
};



class Point 
{
public:
	int x,y,z; 
	Point(int ix=0,int iy=0,int iz=0);
	int XDist(int);
	int YDist(int);
	double Dist(int,int);
	
	int XDist(Point p);
	int YDist(Point p);
	double Dist(Point p);
};


class ImageOperator
{
protected:
	int Rows,Cols;
	float **dat;
	float Sum;
	
	void CalculateSum();
	void Create(int sr,int sc);
	void Destroy();
	
public:
	ImageOperator() {Rows=Cols=0; dat=new float *[0];}
	ImageOperator(int sr,int sc) { ImageOperator(); Create(sr,sc); }
	~ImageOperator() {Destroy(); }
	float GetWeight(int r,int c) {return dat[r][c];}
	float GetTotal() {return Sum;}
	int GetRows() {return Rows;}
	int GetCols() {return Cols;}
	
	void CreateGaussian(float pix);
	void CreateGaussian(int xl,int yl);
	void CreateMean(int sx,int sy);
	friend class Image;
};


class Histogram
{
	friend class Image;
protected:
	int Data[256];
	int Peak;
public:
	Histogram();
	Histogram(Image &img,RMETHOD rmet=RMD_REDC);
	~Histogram();
	void Clear();
	int GetBottom(float per=0.5);
	int GetTop(float per=0.5);
};

class RGBHistogram
{
	friend class Image;
protected:
	Histogram RHist,GHist,BHist;
public:
	RGBHistogram();
	RGBHistogram(Image &img);
	void Clear();
	ImgRGBColor GetBottom(float per=0.5);
	ImgRGBColor GetTop(float per=0.5);
};


class Image
{
protected:
	
	
	
	int      Flag;
	BYTE    *Data;
	char     fname[MAX_PATH];
	ImgRGBColor *pal;
	RGBMask  Mask;
	int      Bpp,Pitch;
	int      xlen,ylen;
	int	     xpos,ypos,vxlen,vylen;
	ITYPE itype;
	
	// Internal memory Management
	int LockStack;
	virtual void IAlloc();
	virtual void IFree();
	virtual void SetData(BYTE *); //assume to be corrrctly for formated with Pitch=xlen
	virtual void RealizePalette(); //makes the new pallette take effect
	void AllocPal() {if(pal) return; else pal=new ImgRGBColor[256];}
	// Internal data Management
	void Init();  
	void CorrectPitch();  //corrects the pitch of loaded image
	//Multiple or incorrect calls will corrupt data
	void DeInterlace();   //deinterlaces a 4 interlaced image
	void DePitch(BYTE *);  //fills buffer with copy of depitched data
	void MaskFrom(RGBMask &imask);  //Remask the data from this mask to correct mask
	
	//Internal Manipulation
	void Difference();
	void UnDifference();
	void InternalLoadPng();
	void InternalSavePng();
public:
	void FillHistogram(Histogram *hist,RMETHOD rmet=RMD_REDC);
	void FillHistogram(RGBHistogram *hist);
	
	void MaskTo(RGBMask &omask);  //Remask the data from this mask to correct mask
	int GetXPos()   {return xpos;}
	int GetYPos()   {return ypos;}
	void ResetPos()  {xpos=ypos=0;}
	void IgnoreVDims()  {xpos=ypos=0; vxlen=xlen; vylen=ylen;}
	
	int GetHeight() {return ylen;}
	int GetWidth()  {return xlen;}
	int GetVHeight() {return vylen;}
	int GetVWidth()  {return vxlen;}
	int GetPitch()	{return Pitch;}
	int GetIType()  {return itype;}
	int GetBpp()    {return Bpp;}
	char *GetFileName()   {return fname;}
	
	Image();
	Image(Image &img);
	Image(char *);
	Image(int xl,int yl);
	Image(int xl,int yl,ImgRGBColor col);
	
	void Delete();
	int IsData();
	
	bool HasAlpha() {return Mask.GetABits()!=0;}
	//>>>>>>>>>>>>>>>>>>>> creation
	void CreateGrid(int xlen=320,int ylen=200,int gx=10,int gy=10,ImgRGBColor bgk=ImgRGBColor(0,0,0),ImgRGBColor frg=ImgRGBColor(255,255,255),ITYPE itp=ITYPE_RGB,int bpp=24);
	void CreateFade(int xl=320,int yl=200,ImgRGBColor rgb1=ImgRGBColor(255,255,255),ImgRGBColor rgb2=ImgRGBColor(0,0,0),int ort=0,ITYPE itp=ITYPE_RGB,int bpp=24);
	void CreateSolid(int xl=320,int yl=200,ImgRGBColor col=ImgRGBColor(0,0,0),ITYPE itp=ITYPE_RGB,int bpp=24);
	void CreateBlank(int xl=320,int yl=200,ITYPE itp=ITYPE_RGB,int bpp=24);
	void CreateBlank(int xl,int yl,ITYPE itp,int bpp,RGBMask &msk);
	
	
	void FillColor(ImgRGBColor fcol=ImgRGBColor(0,0,0));
	
	//>>>>>>>>>>>>>>>>>>>> enhancment
	void Swap(const ImgRGBColor &,const ImgRGBColor &);
	void ReplaceAlpha(int acutoff=0,const ImgRGBColor &bcol=ImgRGBColor(0,0,0));  //Replaces alpha <= this value with color
	void GaussianBlur(float pix);
	void MeanBlur(int xs,int ys);
	void ApplyOperator(ImageOperator &Opt);
	void Pad(int pixs=1);
	void MakeSlopeMap(float xscale=1.0f,float yscale=1.0f,float zscale=1.0f,ImgRGBColor lowcol=ImgRGBColor(255,0,0),ImgRGBColor highcol=ImgRGBColor(255,0,0));
	void MakeContourMap(float xscale=1.0f,float yscale=1.0f,float zscale=1.0f,int step=10,ImgRGBColor bgcol=ImgRGBColor(0,0,0),ImgRGBColor lolinecol=ImgRGBColor(255,255,255),ImgRGBColor highlinecol=ImgRGBColor(255,255,255));
	void ShowSlopeLimit(float xscale=1.0f,float yscale=1.0f,float zscale=1.0f,float limit=45.0f,ImgRGBColor lowcol=ImgRGBColor(255,0,0),ImgRGBColor highcol=ImgRGBColor(255,0,0));
	float GetBrightness();
	void BrightenBy(float bght);
	void ContrastBy(float bght);
	void AdjustToRange(ImgRGBColor &maxcol,ImgRGBColor &mincol);
	void AutoAdjust();
	
	//>>>>>>>>>>>>>>>>>>>> Memory Management
	virtual BYTE *Lock();
	virtual void UnLock();
	
	//>>>>>>>>>>>>>>>>>>>> External Pallete Management
	virtual void SetPaletteEntry(int idx,int r,int g ,int b); 
	void SetPaletteEntry(int idx,float r,float g,float b); 
	void SetPaletteEntry(int,const ImgRGBColor &rgb); 
	ImgRGBColor GetPaletteEntry(int); 
	void MakeGrayscalePalette();
	void MakePalette(ImgRGBColor low,ImgRGBColor high);
	
	
	//>>>>>>>>>>>>>>>>>>>> manipulation
	void MakeRGB(RGBMask msk=RGBMask(0xff0000,0x00ff00,0x0000ff,0));
	void MakeRGB24();
	void MakeRGB16(RGBMask msk=RGBMask(0xf800,0x07E0,0x001f,0));
	void MakeBW();
	void Invert();
	void Quantize();
	void Crop(int x1,int y1,int x2, int y2);
	Point Crop(ImgRGBColor ccl=ImgRGBColor(0,0,0));
	void ReduceTo8Bit(RMETHOD rm);
	
	
	void Blt(Image &img,RECT &dst);
	void Blt(Image &img,RECT dst,RECT &src);
	void Blt(Image &img,FRect dst);
	void Blt(Image &img,FRect dst,FRect src);
	void Blt(Image &img,int x=0,int y=0);
	void Blt(Image &img,int x,int y,RECT &src);
	void Blt(Image &img,int x,int y,FRect src);
	
	void BltDominantColor(Image &img,RECT dst,RECT &src);
	void BltBlend(Image &img,float efact,RECT &src,float tfact,float rfact,float bfact,float lfact);
	// Blends edges of image into other images that are sampled differently
	// efact = % of edge to blend 0-1
	// tfact = reduction coefficent of above image <0 if same as this image
	
	void Rotate180();
	
	//>>>>>>>>>>>>>>>>>>>> Super Cool Manipulations
	
	void CreateDifferentialMap();
	void CreateDifferentialMap(float *, int);
	
	//>>>>>>>>>>>>>>>>>>>> File management
	void Load(FILE *);
	void Load(char *);
	void Load();
	
	int IsTiff(FILE *);
	void LoadTiff(FILE *);
	void LoadTiff(char *);
	void SaveTiff(FILE *);
	void SaveTiff(char *);
	
	
	int IsGif(FILE *fl);
	void LoadGif(FILE *);
	void LoadGif(char *);
	void SaveGif(FILE *);
	void SaveGif(char *);
	
	int IsTga();
	void LoadTga(FILE *);
	void SaveTga(FILE *);
	void GetTgaInfo(FILE *);
	void SaveTga(char *);
	void LoadTga(char *);
	void GetTgaInfo(char *);
	void LoadTga(Stuff::FileStream *stream);
	void GetTgaInfo(Stuff::FileStream *stream);
	void SaveTga(Stuff::FileStream *stream);
	
	int IsPng();
	int IsPng(FILE *fl);
	void LoadPng(FILE *);
	void SavePng(FILE *);
	void GetPngInfo(FILE *);
	void LoadPng(char *);
	void SavePng(char *);
	void GetPngInfo(char *);
	int IsPng(Stuff::FileStream *stream);
	void LoadPng(Stuff::FileStream *stream);
	void SavePng(Stuff::FileStream *stream);
	void GetPngInfo(Stuff::FileStream *stream);
	
	void SaveRaw(FILE *);
	void SaveRaw(char *);
	
	//>>>>>>>>>>>>>>>>>>>> matinaance
	ImgRGBColor GetColor(Point &p);
	Image &operator=(Image &img);
	bool operator==(Image &img);
	bool operator!=(Image &img) {return !(*this==img);}
	
	~Image();
	friend class OctQuant;          // Octree color quantization
};

//Inline functions

__inline void RGBPixel::Set(const ImgRGBColor &col,const RGBMask &mask)
{
	switch(mask.bits)
	{
	case 16:
		wrd[0]=(WORD)(((col.r>>mask.ras)<<mask.irs)|((col.g>>mask.gas)<<mask.igs)|
			((col.b>>mask.bas)<<mask.ibs)|((col.a>>mask.aas)<<mask.ias)); 
		break;
		
	case 24:
		{
			unsigned long int tdw;
			tdw=((col.r>>mask.ras)<<mask.irs)|((col.g>>mask.gas)<<mask.igs)|
				((col.b>>mask.bas)<<mask.ibs)|((col.a>>mask.aas)<<mask.ias);
			wrd[0]=(unsigned short)(tdw&0xffff);
			com[2]=(unsigned char)((tdw>>16)&0xff);
		}
		break;
		
	case 32:
		dword=((col.r>>mask.ras)<<mask.irs)|((col.g>>mask.gas)<<mask.igs)|
			((col.b>>mask.bas)<<mask.ibs)|((col.a>>mask.aas)<<mask.ias);
		break;
		
	default:
		GENFERR("SetPixel Unsupported number of bits.");
		break;
	}
}


__inline void RGBPixel::Set(const ImgRGBColor &col)
{
	RGBMask m(0xff0000,0x00ff00,0x0000ff);
	Set(col,m);
}


// ImgRGBColor Functions

__inline void ImgRGBColor::Set(const RGBPixel &pix,const RGBMask &mask)				 
{ 
	
	Set(
		((pix.dword&mask.R)>>mask.irs)<<mask.ras,
		((pix.dword&mask.G)>>mask.igs)<<mask.gas,
		((pix.dword&mask.B)>>mask.ibs)<<mask.bas,
		((pix.dword&mask.A)>>mask.ias)<<mask.aas
		);
}

__inline void ImgRGBColor::Bound()
{
	if(r>255) r=255; else if(r<0) r=0;
	if(g>255) g=255; else if(g<0) g=0;
	if(b>255) b=255; else if(b<0) b=0;
}

__inline void ImgRGBColor::Merge(const ImgRGBColor &ac,int dw,int sw)	//weighted merge
{
	r=(r*dw+ac.r*sw)/(dw+sw); 
	g=(g*dw+ac.g*sw)/(dw+sw); 
	b=(b*dw+ac.b*sw)/(dw+sw);
}

__inline void ImgRGBColor::Blend(const ImgRGBColor &ac,float trans)	//weighted merge
{
	r=(unsigned long)(r*(1-trans)+ac.r*trans);
	g=(unsigned long)(g*(1-trans)+ac.g*trans);
	b=(unsigned long)(b*(1-trans)+ac.b*trans);
}
#endif
