///////////////////////////////////////////////////////////////////////////////
// CDraw32 Class Interface
//
// Basic drawing routines for 32-bit per pixel buffer
///////////////////////////////////////////////////////////////////////////////

#if !defined(CM_DRAW_H_INC)
#define CM_DRAW_H_INC

// calc offset into image array for a pixel at (x,y)
#define PIXPOS(x,y,stride) (((y)*(stride))+(x))

#ifndef MIN
// handy macros
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define ABS(x) ((x)<0 ? -(x):(x))
#define SIGN(x)  (((x) < 0) ? -1 : (((x) > 0) ? 1 : 0))
#endif

#ifndef CLAMP
#define SWAP(a,b) { a^=b; b^=a; a^=b; }
#define SQR(a)  ((a)*(a))
#define CLAMP(v,l,h)    ((v)<(l) ? (l) : (v) > (h) ? (h) : (v))
#define LERP(t, a, b) (((b)-(a))*(t) + (a))

// round a to nearest integer towards 0
#define FLOOR(a)        ((a)>0 ? (int)(a) : -(int)(-a))

// round a to nearest integer away from 0
#define CEILING(a) \
((a)==(int)(a) ? (a) : (a)>0 ? 1+(int)(a) : -(1+(int)(-a)))

#ifndef WIN32
typedef struct {
	int x;
	int y;
} POINT;
#endif

#include <stdlib.h>
#endif

class CPixel32
{
public:
	byte r;
	byte g;
	byte b;
	byte a;

	CPixel32(byte R = 0, byte G = 0, byte B = 0, byte A = 255) : r(R), g(G), b(B), a(A) {}
	CPixel32(long l) {r = (l >> 24) & 0xff; g = (l >> 16) & 0xff; b = (l >> 8) & 0xff; a = l & 0xff;};

	~CPixel32()
	{}
};

#define PIX32_SIZE sizeof(CPixel32)

// standard image operator macros
#define IMAGE_SIZE(width,height) ((width)*(height)*(PIX32_SIZE))


inline CPixel32 AVE_PIX (CPixel32 x, CPixel32 y) 
	{ CPixel32 t; t.r = (byte)(((int)x.r + (int)y.r)>>1); 
				  t.g = (byte)(((int)x.g + (int)y.g)>>1); 
				  t.b = (byte)(((int)x.b + (int)y.b)>>1); 
				  t.a = (byte)(((int)x.a + (int)y.a)>>1); return t;}

inline CPixel32 ALPHA_PIX (CPixel32 x, CPixel32 y, long alpha, long inv_alpha) 
	{ CPixel32 t; t.r = (byte)((x.r*alpha + y.r*inv_alpha)>>8); 
				  t.g = (byte)((x.g*alpha + y.g*inv_alpha)>>8); 
				  t.b = (byte)((x.b*alpha + y.b*inv_alpha)>>8); 
//				  t.a = (byte)((x.a*alpha + y.a*inv_alpha)>>8);  return t;}
				  t.a = y.a;  return t;}

inline CPixel32 LIGHT_PIX (CPixel32 p, long light) 
{ CPixel32 t; 
  t.r = (byte)CLAMP(((p.r * light)>>10) + p.r, 0, 255); 
  t.g = (byte)CLAMP(((p.g * light)>>10) + p.g, 0, 255); 
  t.b = (byte)CLAMP(((p.b * light)>>10) + p.b, 0, 255); 
  t.a = p.a;  return t;}

// Colors are 32-bit RGBA

// draw class
class CDraw32
{
public: // static drawing context - static so we set only ONCE for many draw calls
	static	CPixel32*	buffer;	   			// pointer to pixel buffer (one active)
	static	long	buf_width;				// size of buffer
	static	long	buf_height;				// size of buffer
	static	long	stride;					// stride of buffer in pixels
	static	long	clip_min_x;				// clip bounds
	static	long	clip_min_y;				// clip bounds
	static	long	clip_max_x;				// clip bounds
	static	long	clip_max_y;				// clip bounds
	static	long*	row_off;				// Table for quick Y calculations

private:
	void BlitClip(long& dstX, long& dstY, 
		 		  long& width, long& height, 
				  long& srcX, long& srcY);

protected:
public:
	CDraw32();						// constructor
	~CDraw32();						// destructor

	// set the rect to clip drawing functions to
	static void	SetClip(long min_x, long min_y,long max_x, long max_y)
						{clip_min_x = MAX(min_x,0); clip_max_x = MIN(max_x,buf_width-1);
						 clip_min_y = MAX(min_y,0); clip_max_y = MIN(max_y,buf_height-1);}

	static void GetClip(long& min_x, long& min_y,long& max_x, long& max_y)
						{min_x = clip_min_x; min_y = clip_min_y;
						 max_x = clip_max_x; max_y = clip_max_y; }

	// set the buffer to use for drawing off-screen
	static void	SetBuffer(CPixel32* buf) {buffer = buf;};

	// set the dimensions of the off-screen buffer
	static bool	SetBufferSize(long width,long height,long stride_len);

	// call this to free the table for quick y calcs before the program ends
	static void CleanUp(void) 
		{if (row_off) delete [] row_off; row_off=NULL; buf_width=0; buf_height=0;}

	// set a pixel at (x,y) to color (no clipping)
	void				PutPixNC(long x, long y, CPixel32 color)
		{buffer[row_off[y] + x] = color;}

	// set a pixel at (x,y) to color
	void				PutPix(long x, long y, CPixel32 color)
		{	// clipping check
			if (x < clip_min_x || x > clip_max_x ||
			    y < clip_min_y || y > clip_max_y)
	  			return;
			PutPixNC(x,y,color);
		}

	// get the color of a pixel at (x,y)
	CPixel32			GetPix(long x, long y)
		{return buffer[row_off[y] + x];}

	// set a pixel at (x,y) with 50% translucency (no clip)
	void				PutPixAveNC(long x, long y, CPixel32 color)
		{	PutPixNC(x,y,AVE_PIX(GetPix(x, y), color)); }

	// set a pixel at (x,y) with 50% translucency
	void				PutPixAve(long x, long y, CPixel32 color)
		{	// clipping check
			if (x < clip_min_x || x > clip_max_x ||
				y < clip_min_y || y > clip_max_y)
		  		return;
			PutPixNC(x,y,AVE_PIX(GetPix(x, y), color));
		}

	// set a pixel at (x,y) with translucency level (no clip)
	void				PutPixAlphaNC(long x, long y, CPixel32 color)
		{	PutPixNC(x,y,ALPHA_PIX(color, GetPix(x, y), color.a, 256-color.a));}

	// set a pixel at (x,y) with translucency level
	void				PutPixAlpha(long x, long y, CPixel32 color)
		{	// clipping check
		if (x < clip_min_x || x > clip_max_x ||
		    y < clip_min_y || y > clip_max_y)
  		return;
		PutPixNC(x,y,ALPHA_PIX(color, GetPix(x, y), color.a, 256-color.a));}

	// clear screen buffer to color from start to end line
	void				ClearLines(CPixel32 color,long start,long end);

	// clear screen buffer to color provided
	void				ClearBuffer(CPixel32 color)
		{ClearLines(color,0,buf_height-1);};

	// fill buffer alpha from start to end line
	void				SetAlphaLines(byte alpha,long start,long end);

	// clear screen buffer to color provided
	void				SetAlphaBuffer(byte alpha)
		{SetAlphaLines(alpha,0,buf_height-1);};

	// clip a line segment to the clip rect
	bool				ClipLine(long& x1, long& y1, long& x2, long& y2);

	// draw a solid colored line, no clipping
	void				DrawLineNC(long x1, long y1, long x2, long y2, CPixel32 color);
	
	// draw a solid color line
	void				DrawLine(long x1, long y1, long x2, long y2, CPixel32 color)
		{	if (ClipLine(x1,y1,x2,y2)) DrawLineNC(x1,y1,x2,y2,color);}

	void				DrawLineAveNC(long x1, long y1, long x2, long y2, CPixel32 color);

	// draw a translucent solid color line
	void				DrawLineAve(long x1, long y1, long x2, long y2, CPixel32 color)
		{	if (ClipLine(x1,y1,x2,y2)) DrawLineAveNC(x1,y1,x2,y2,color);}

	// draw an anti-aliased line, no clipping
	void				DrawLineAANC(long x0, long y0, long x1, long y1, CPixel32 color);

	// draw an anti-aliased line
	void				DrawLineAA(long x1, long y1, long x2, long y2, CPixel32 color)
		{	if (ClipLine(x1,y1,x2,y2)) DrawLineAANC(x1,y1,x2,y2,color);}

	// draw a filled rectangle, no clipping
	void				DrawRectNC(long ulx, long uly, long width, long height,CPixel32 color);
	
	// draw a filled rectangle
	void				DrawRect(long ulx, long uly, long width, long height, CPixel32 color);

	// draw a filled rectangle
	void				DrawRectAve(long ulx, long uly, long width, long height,CPixel32 color);

	// draw a box (unfilled rectangle) no clip
	void				DrawBoxNC(long ulx, long uly, long width, long height, CPixel32 color);

	// draw a box (unfilled rectangle)
	void				DrawBox(long ulx, long uly, long width, long height, CPixel32 color);

	// draw a box (unfilled rectangle)
	void				DrawBoxAve(long ulx, long uly, long width, long height, CPixel32 color);

	// draw a circle with fill and edge colors 
	void				DrawCircle(long xc, long yc, long r, CPixel32 edge, CPixel32 fill);

	// draw a circle with fill and edge colors averaged with dest 
	void				DrawCircleAve(long xc, long yc, long r, CPixel32 edge, CPixel32 fill);

	// draw a polygon (complex) with fill and edge colors 
	void				DrawPolygon(long nvert, POINT *point, CPixel32 edge, CPixel32 fill);

	// simple blit function
	void				BlitNC(long dstX, long dstY, long dstWidth, long dstHeight, 
							   CPixel32* srcImage, long srcX, long srcY, long srcStride);

	void				Blit(long dstX, long dstY, long dstWidth, long dstHeight, 
							   CPixel32* srcImage, long srcX, long srcY, long srcStride);

	// blit image times color
	void				BlitColor(long dstX, long dstY, long dstWidth, long dstHeight, 
								  CPixel32* srcImage, long srcX, long srcY, long srcStride, CPixel32 color);

	void				Emboss(long dstX, long dstY, long width, long height, 
							    CPixel32* clrImage, long clrX, long clrY, long clrStride);
};

///////////////////////////////////////////////////////////////////////////////
#endif