#pragma once

#ifndef PI
#define PI 3.1415926535897932384626433832795f
#endif


typedef IDirect3DTexture8 D3DTexture;
typedef IDirect3DSurface8 D3DSurface;
typedef IDirect3DVertexBuffer8 D3DVBuf;
typedef IDirect3DIndexBuffer8 D3DIBuf;

typedef unsigned char u8;

#define OSCAR_API 

#include "vector3.h"
#include "vector4.h"
#include "matrix44.h"

typedef Matrix44 m44;
typedef D3DXVECTOR2 v2;
typedef Vector3 v3;
typedef Vector4 v4;

extern v3 sunpos,shadmin,shadmax;
extern int halfmode;

#define bound(min,x,max) ((x)<(min)?(min):(x)>(max)?(max):(x))
#define RELEASE(x) if (x) (x)->Release();x=NULL;

#define MAXTRIS 8192

__inline float Random(float mn=-1, float mx=1)
{
	return mn+rand()*(mx-mn)/32767.f;
}

__inline float GaussRandom(float mn=-1, float mx=1, int iter=6)
{
	float f=0;
	for (int c1=0;c1<iter;c1++) f+=Random(mn,mx);
	return f/iter;
}


//namespace D3D
//{
	extern int ownhwnd;
	extern float jitterx;
	extern float jittery;
	
	
	int Init(HWND hwnd, int xres, int yres);
	void Close();
	void ResetState();

	void SetupPixelFog(DWORD Color, DWORD Mode, float start = 10.f, float end=100.f);

	void SetWorldMat();
	void SetCam();	
	void SetFOV();

	void SetWorldMat(m44 mat);
	void SetFOV(float fov,float minz=0.1f, float maxz=10000.f);
	void SetCam(m44 cammat);
	void SetCam(v3 campos, v3 camfoc, v3 up=v3(0,1,0));

	void Flip();



	extern float camfov;
	extern float camminz;
	extern float cammaxz;
	extern v3 campos;
	extern v3 camup;
	extern v3 camfoc;
	extern m44 cammat;
	extern m44 worldmat;
	extern m44 projmat;
	extern m44 blobmat;
	extern m44 worldviewmat;	


	extern int inited;
	extern D3DDISPLAYMODE d3ddm;
	extern D3DPRESENT_PARAMETERS d3dpp;
	extern LPDIRECT3D8             d3d ; // Used to create the D3DDevice
	extern LPDIRECT3DDEVICE8       d3ddev ; // Our rendering device

	struct SimpleVert
	{
		v3 p;
		int col;
		float u,v;
		float u2,v2;
		float u3,v3;
	};

	
	struct SimpleRHWVert
	{
		v3 p;
		float rhw;
		int col;
		float u,v;
	};

	


	struct pixel 
	{
		union
		{
			struct
			{
				u8 b,g,r,a;
			};
			u8 chan[4];
			int c;
		};

		static __inline u8 sat(int a)
		{
			if (a&0xffffff00)
			{
				return ~(a>>31);
			}
			else return a;
		}

				
		operator int()
		{
			return c;
		}
		pixel(int cc=0)
		{
			c=cc;
		}
		pixel(float fr, float fg, float fb, float fa=1.f)
		{
			r=u8(bound(0.f,fr,1.f)*255);
			g=u8(bound(0.f,fg,1.f)*255);
			b=u8(bound(0.f,fb,1.f)*255);
			a=u8(bound(0.f,fa,1.f)*255);
		}
		pixel(u8 rr, u8 gg, u8 bb, u8 aa=255)
		{
			r=rr;g=gg;b=bb;a=aa;
		}
		void Set(int rr, int gg, int bb, int aa=255)
		{
			r=bound(0,rr,255);
			g=bound(0,gg,255);
			b=bound(0,bb,255);
			a=bound(0,aa,255);
		}
		void Set(float fr, float fg, float fb, float fa=1.f)
		{
			r=u8(bound(0.f,fr,1.f)*255);
			g=u8(bound(0.f,fg,1.f)*255);
			b=u8(bound(0.f,fb,1.f)*255);
			a=u8(bound(0.f,fa,1.f)*255);
		}
		__inline static u8 clamp(int a)
		{
			return bound(0,a,255);
		}
		__inline static u8 clamp(int a, int b)
		{
			return bound(0,a+b,255);
		}
		__inline static u8 clamp(int a, int b, int c)
		{
			a = ((a*b)>>8)+c;
			return bound(0,a,255);
		}

		u8 &operator [] (int idx) 
		{
			return chan[idx&3];
		}		
		pixel operator +(const pixel &p) const
		{
			return pixel(clamp(r,p.r),clamp(g,p.g),clamp(b,p.b),clamp(a,p.a));
		}
		pixel operator -(const pixel &p) const
		{
			return pixel(clamp(r,-int(p.r)),clamp(g,-int(p.g)),clamp(b,-int(p.b)),clamp(a,-int(p.a)));
		}
		pixel operator *(const int m) const
		{
			return pixel(clamp((r*m)>>8),clamp((g*m)>>8),clamp((b*m)>>8),clamp((a*m)>>8));
		}
		pixel &operator +=(const pixel &p)
		{
			r=clamp(r,p.r);
			g=clamp(g,p.g);
			b=clamp(b,p.b);
			a=clamp(a,p.a);
			return (*this);
		}
		pixel &operator -=(const pixel &p)
		{
			r=clamp(r,-int(p.r));
			g=clamp(g,-int(p.g));
			b=clamp(b,-int(p.b));
			a=clamp(a,-int(p.a));
			return (*this);
		}
		pixel &operator *=(const int p)
		{
			r=clamp(r*p);
			g=clamp(g*p);
			b=clamp(b*p);
			a=clamp(a*p);
			return (*this);
		}
		__inline static pixel alphablend(const pixel &l, const pixel &r, const int alpha)
		{
			if (alpha<=0) return l;
			if (alpha>255) return r;
			return pixel(clamp((r.r-l.r),alpha,l.r),
						 clamp((r.g-l.g),alpha,l.g),
						 clamp((r.b-l.b),alpha,l.b),
						 clamp((r.a-l.a),alpha,l.a));
		}

		
			

		
	};

	__inline int alphablend(const int l, const int r, const int alpha)
	{
		if (alpha<=0) return l;
		if (alpha>255) return r;
		return pixel::clamp((r-l),alpha,l);	
	}

	enum OperType
	{
		OPER_SET,
		OPER_ADD,
		OPER_SUB,
		OPER_REVSUB,
		OPER_SIGNADD,
		OPER_MUL,
		OPER_MUL2X,
		OPER_MUL4X,
		OPER_OVERLAY,
		OPER_SCREEN,		
		OPER_HARDLIGHT, // overlay the other way round
		OPER_MIN,
		OPER_MAX,
		OPER_LAST,
	};

	/*
	enum Chan
	{
		CHAN_B = 1,
		CHAN_G = 2,
		CHAN_R = 4,
		CHAN_A = 8,
		CHAN_COL = 7,
		CHAN_RGB = 7,
		CHAN_ALL = 15,
		CHAN_RGBA = 15,
	};
	*/

	void UpdateJitter(int jitterc);

	struct Surface
	{
		int w,h;
		pixel *pix;
		pixel *pix2;
		
		D3DSurface *rtarget;
		D3DSurface *zbuf;
		D3DTexture *tex;
		bool isbackbuf;
		

		Surface(const Surface &src)
		{
			CopyFrom(src);
		}
		Surface &operator = (const Surface &src) 
		{
			CopyFrom(src);
		}
		void CopyFrom(const Surface &src);
		Surface(int ww=0, int hh=0);
		~Surface();
		void CreateFromBackBuf();
		void SetSize(int ww, int hh);

		void Halve();

		void LoadFromDisk(char *fname);
		void UpdateFromTexture();
		void UpdateFromRendertarget();
		void UpdateToTexture();
		void UpdateToRendertarget(Surface *src=NULL);
		void ClearPix(int col);

		Surface *lasttarget;

		static Surface *curtarget;
		static Surface *backbufsurf;

		
		
		void BeginFrame(bool clearz=true, bool clearback=true, int clearcol=0x123456);
		void EndFrame(bool updatefromrendertarget, bool updatetotexture=NULL);

		// operations
		void Blt(bool doalpha, OperType oper, int operalpha, Surface *src, int dstx=0, int dsty=0, int srcx=0, int srcy=0, int srcw=0, int srch=0);		
		void Tint(OperType oper, int operalpha, pixel col, int x=0, int y=0, int w=0, int h=0);
		
		void GaussBlur(pixel *dst, float k);
		void BoxBlur(pixel *dst, int kx, int ky);

		void BoxHBlur	(pixel*src,pixel*dst,int k);
		void BoxVBlur	(pixel*src,pixel*dst,int k);
		void GaussHBlur	(pixel*src,pixel*dst,float k);
		void GaussVBlur	(pixel*src,pixel*dst,float k);
		//void Median(OperType oper, int operalpha, int kx, int ky, int chan);		 TODO
		void Curves(pixel *curves);
		void BrightnessContrast(float brightness=0, float contrast=1);

		void RGB2HSV();
		void HSV2RGB();
		
		// internal
		void Reset();
		void CreatePix();
		void CreateTex();
		void CreateRenderTarget();
		void SwapPix()
		{
			pixel *t=pix;pix=pix2;pix2=t;
		}

		static u8 opertable[OPER_LAST][256][256];
		static void InitOper();

		__inline void OperateAlpha(OperType oper, int operalpha, pixel*dst, pixel*src1, pixel*src2, int count=1)
		{

			for (int p=0;p<count;p++)
			{
				pixel pp;
				int xoperalpha = (operalpha * src2->a)>>8;
				for (int c=0;c<4;c++) //if (chan&(1<<c))
				{
					
					pp[c]=alphablend((*src1)[c],opertable[oper][(*src2)[c]][(*src1)[c]],xoperalpha);
				}
				/*
				else
				{
					pp[c]=(*src1)[c];
				}
				*/
				*dst++=pp;
				src1++;
				src2++;				
			}
		}

		__inline void Operate(OperType oper, int operalpha, pixel*dst, pixel*src1, pixel*src2, int count=1)
		{

			for (int p=0;p<count;p++)
			{
				pixel pp;				
				for (int c=0;c<3;c++) //if (chan&(1<<c))
				{
					
					pp[c]=alphablend((*src1)[c],opertable[oper][(*src2)[c]][(*src1)[c]],operalpha);
				}
				pp.a=255;
				/*
				else
				{
					pp[c]=(*src1)[c];
				}
				*/
				*dst++=pp;
				src1++;
				src2++;				
			}
		}

	};

	#define SimpleVertFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX3)
#define SimpleVertUV2FVF (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX3)
#define SimpleRHWVertFVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)

struct SimpleBuffer2D;
struct SimpleBuffer;
struct SimpleBufferUV2;

#define RHW
#define SIMPLEBUFFER SimpleBuffer2D
#define SIMPLEVERT SimpleRHWVert
#define SIMPLEFVF SimpleRHWVertFVF
#include "simplebuffer.h"

#define SIMPLEBUFFER SimpleBuffer
#define SIMPLEVERT SimpleVert
#define SIMPLEFVF SimpleVertFVF
#include "simplebuffer.h"

#define SIMPLEBUFFER SimpleBufferUV2
#define SIMPLEVERT SimpleVert
#define SIMPLEFVF SimpleVertUV2FVF
#define UV2
#include "simplebuffer.h"


//}


