#ifndef GE_IFACE_H
#define GE_IFACE_H

/*
	Ripatti Artem (c) 2011

	Тут содержится
		описание структуры PICT
		описание интерфейса XGraphicEngine
*/

#include <math.h>

typedef unsigned long	DWORD;
typedef unsigned char	BYTE;

namespace ge
{
	#define _a(x) (BYTE)(x>>24)
	#define _r(x) (BYTE)(x>>16)
	#define _g(x) (BYTE)(x>> 8)
	#define _b(x) (BYTE)(x>> 0)
	#define rgb(r, g, b) (DWORD)((0xff<<24) + (r<<16) + (g<<8) + (b<<0))
	#define argb(a, r, g, b) (DWORD)((a<<24) + (r<<16) + (g<<8) + (b<<0))

	/*
		двумерная вершина полигона
	*/
	struct P
	{
		float x, y;			// коордитаты
		DWORD clr;	// цвет, на который умножается вершина (в том числе и прозрачность)
		float tx, ty;		// текстурные координаты
	};

	/*
		каждая картинка представляет собой четырехугольник
		v0-----v1
		|      /
		|     /
		|    /
		v2-v3
		координаты, цвет и текстурные координаты можно менять как угодно.
		таким образом, помимо ровных прямоугольников, можно делать, например, трапеции,
		делать их заливку градиентом (в том числе градиентно менять прозрачность)
	*/
	struct PICT
	{
		P v[4];

		// создает прямоугольную картинку размера sx на sy,
		// верхний левый угол которой имеет координаты (x,y).
		PICT(float x, float y, float sx, float sy)
		{
			float tx = 0.f, ty = 0.f, tx1 = 1.f, ty1 = 1.f;

			v[0].x = x;			v[0].y = y;
			v[0].tx = tx;		v[0].ty = ty;
			v[0].clr = rgb(255,255,255);

			v[1].x = x + sx;	v[1].y = y;
			v[1].tx = tx + tx1;	v[1].ty = ty;
			v[1].clr = rgb(255,255,255);

			v[2].x = x;			v[2].y = y + sy;
			v[2].tx = tx;		v[2].ty = ty + ty1;
			v[2].clr = rgb(255,255,255);

			v[3].x = x + sx;	v[3].y = y + sy;
			v[3].tx = tx + tx1;	v[3].ty = ty + ty1;
			v[3].clr = rgb(255,255,255);
		}

		// установить текстуру в виде прямоугольной области
		PICT & set_tex( float tx, float ty, float sx, float sy )
		{
			v[0].tx = tx;		v[0].ty = ty;
			v[1].tx = tx + sx;	v[1].ty = ty;
			v[2].tx = tx;		v[2].ty = ty + sy;
			v[3].tx = tx + sx;	v[3].ty = ty + sy;

			return * this;
		}

		// повернуть на угол angle по часовой стрелке
		// angle измеряется в градусах
		PICT & rotate(int angle)
		{
			float cx = 0.25f*(v[0].x + v[1].x + v[2].x + v[3].x);
			float cy = 0.25f*(v[0].y + v[1].y + v[2].y + v[3].y);

			float dx = get_cos(angle);
			float dy = get_sin(angle);

			float x, y;

			for (int a=0; a<4; a++)
			{
				x = v[a].x - cx;
				y = v[a].y - cy;
				v[a].x = cx + x*dx + y*dy;
				v[a].y = cy - x*dy + y*dx;
			}

			return * this;
		}

		// переворачивает картинку относительно вертикальной оси,
		// проходящей через центр масс ее вершин
		// если условие cond выполняется
		PICT & flip_hor(bool cond=true)
		{
			if (cond)
			{
				float cx = 0.25f*(v[0].x + v[1].x + v[2].x + v[3].x);

				for (int a=0; a<4; a++)
					v[a].x = 2*cx - v[a].x;
			}
			return * this;
		}

		// переворачивает картинку относительно горизонтальной оси,
		// проходящей через центр масс ее вершин
		// если условие cond выполняется
		PICT & flip_ver(bool cond=true)
		{
			if (cond)
			{
				float cy = 0.25f*(v[0].y + v[1].y + v[2].y + v[3].y);

				for (int a=0; a<4; a++)
					v[a].y = 2*cy - v[a].y;
			}
			return * this;
		}

		// умножить на цвет
		PICT & mul_to_color( float R, float G, float B, float A )
		{
			for (int a=0; a<4; a++)
				v[a].clr = argb(
					(BYTE)(_a(v[a].clr)*A),
					(BYTE)(_r(v[a].clr)*R),
					(BYTE)(_g(v[a].clr)*G),
					(BYTE)(_b(v[a].clr)*B));
			//		(BYTE)(255*A), (BYTE)(255*R), (BYTE)(255*G), (BYTE)(255*B) );

			return * this;
		}

		static void normalize_angle(int & ang)
		{
			if (0<=ang && ang<360) return;
			ang = (((ang%360)+360)%360);
		}

		// быстрый синус
		// angle - целое количество градусов
		static float get_sin(int angle)
		{
			static bool _sin_calculated = false;
			static float SIN[360];
			if (!_sin_calculated)
			{
				float pi = acos(-1.f);
				for (int a=0; a<360; a++)
					SIN[a] = sin(pi*float(a)/180.f);
				_sin_calculated = true;
			}
			normalize_angle(angle);
			return SIN[angle];
		}

		// быстрый косинус
		// angle - целое количество градусов
		static float get_cos(int angle)
		{
			static bool _cos_calculated = false;
			static float COS[360];
			if (!_cos_calculated)
			{
				float pi = acos(-1.f);
				for (int a=0; a<360; a++)
					COS[a] = cos(pi*float(a)/180.f);
				_cos_calculated = true;
			}
			normalize_angle(angle);
			return COS[angle];
		}
	};

	/*
		интерфейс графического движка

		предполагается, что в движке имеется некоторый набор текстур,
		каждая из которых имеет номер. можно назначать текстуру текущей.
		так же есть текстура рендера, куда идет прорисовка (-1 - если рисуем на экран).
		каждый объект Pict при вызове DrawPicture будет рисоваться на текстуре рендера,
		а сама картинка будет браться из текущей текстуры.
	*/
	class XGraphicEngine
	{
	public:
		// с этого надо начинать фрейм
		virtual void begin_scene()=0;

		// залить все цветом (R,G,B)
		virtual void clear_scene(float R=0.f, float G=0.f, float B=0.f)=0;

		// вывести сцену на экран. этим надо заканчивать фрейм
		virtual void render_scene()=0;

		// назначить текстуру T текущей
		virtual void set_texture(int T)=0;

		// отрисовать картинку Pict
		virtual void draw_pict(ge::PICT & pict)=0;

		// выбор текстуры для отрисовки (-1 - рисовать не в текстуру, а на экран)
		virtual void render_to_texture(int T = -1)=0;

		// написать ch в точке (x,y) цветом (R,G,B,A)
		virtual void write(float x, float y, const char * ch,
			float R=0.f, float G=1.f, float B=0.f, float A=0.94f)=0;
	};

}

#endif
