/*
	Ripatti Artem (c) 2013
*/

#define _CRT_SECURE_NO_WARNINGS

#include "ge_fl_opengl.h"
#include "log.h"
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>

#include "lodepng.h"

using namespace std;

namespace ge
{
	#pragma pack (push,2)
	struct ZBmpFileHeader
	{
		unsigned short	_type;
		unsigned int	_size;
		unsigned short	_reserved1;
		unsigned short	_reserved2;
		unsigned int	_off_bits;
	};

	struct ZBmpInfoHeader
	{
		unsigned int	_size;
		int			    _width;
		int			    _height;
		unsigned short	_planes;
		unsigned short	_bit_count;
		unsigned int	_compression;
		unsigned int	_size_image;
		int	    		_x_pels_per_meter;
		int		    	_y_pels_per_meter;
		unsigned int	_clr_used;
		unsigned int	_clr_important;
	};
	#pragma pack (pop)

	ZGraphicEngineOpenGL::ZGraphicEngineOpenGL( int sx, int sy )
	{
	    Message( "GE", "Loading Graphic Engine..." );
		_sx = (float)sx;
		_sy = (float)sy;
		glLoadIdentity();
		glViewport(0, 0, sx, sy);
		glOrtho(0.0, _sx, 0.0, _sy, -1.0, 1.0);

		glEnable( GL_BLEND );
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

		//glEnable( GL_TEXTURE_2D );
		glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
		glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

		//glReadBuffer( GL_FRONT_AND_BACK );

		//gl_font( FL_COURIER, 11 );

		_cur_t = -1;
		for (int a=0; a<MAX_TEXTURE; a++)
			_tex_id[a] = 0;

		_target_tex = -1;

		make_render_target( 0, 512, 256 );

		Message( "GE", "Graphic Engine is ready!" );
	}

	ZGraphicEngineOpenGL::~ZGraphicEngineOpenGL()
	{
		for (int a=0; a<MAX_TEXTURE; a++)
			if (_tex_id[a])
				glDeleteTextures(1, &_tex_id[a]);
		Message( "GE", "Graphic Engine is destroyed." );
	}

	void ZGraphicEngineOpenGL::make_render_target( int num, int sx, int sy )
	{
		//unsigned char * _tex = new unsigned char[sx*sy*3];
		//memset( _tex, 0, sizeof(unsigned char)*sx*sy*3 );

		//for (int a=0; a<sx*sy*3; a++)
		//	_tex[a] = rand()&255;

		glGenTextures( 1, &_tex_id[num] );
		glBindTexture( GL_TEXTURE_2D, _tex_id[num] );

		glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, sx, sy, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );

		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

		_sz_tex_x[num] = sx;
		_sz_tex_y[num] = sy;
		_sc_tex_x[num] = 1.f;
		_sc_tex_y[num] = 1.f;

		//delete [] _tex;
	}

	void ZGraphicEngineOpenGL::load_texture(int num, const char * path, const char * alpha)
	{
		/*_texture[num] = new unsigned char [64*64*4];
		for (int i=0; i<64; i++)
			for (int j=0; j<64; j++)
			{
				int k = (i*64+j)*4;
				_texture[num][k  ] = (num*47 +  (i/4)*97 + (j/4)*117) & 255;
				_texture[num][k+1] = (num*61 +  (i/4)*61 +  (j/4)*53) & 255;
				_texture[num][k+2] = (num*53 + (i/4)*103 +  (j/4)*91) & 255;
				_texture[num][k+3] = 255;
				if (((i/4)+(j/4)) % 2 == 0)
					_texture[num][k+3] = 128;
			}*/

		ZBmpFileHeader file_hdr;
		ZBmpInfoHeader info_hdr;
		int StX=1, StY=1;

		FILE * f;
		f = fopen( path, "rb" );
		if (f==0) Error("GE", "Cannot open texture file");

		fread(&file_hdr, sizeof(ZBmpFileHeader), 1, f);
		fread(&info_hdr, sizeof(ZBmpInfoHeader), 1, f);

        //cout << sizeof( long ) << " " << sizeof( int ) << " " << sizeof( short ) << "\n";
        //cout << file_hdr._type << " " << info_hdr._bit_count << " " << info_hdr._compression << "\n";
        //cout << info_hdr._width << " " << info_hdr._height << "\n";
		if (file_hdr._type != 19778 || info_hdr._bit_count != 24 || info_hdr._compression != 0)
			Error("GE", "Do not know this format");

		while (StX < info_hdr._width) StX*=2;
		while (StY < info_hdr._height) StY*=2;
		_sc_tex_x[num] = (float)(info_hdr._width)/(float)(StX);
		_sc_tex_y[num] = (float)(info_hdr._height)/(float)(StY);

		unsigned char * _tex = new unsigned char [StX*StY*4];
		_sz_tex_x[num] = StX;
		_sz_tex_y[num] = StY;

		unsigned char c[4];
		for (int y = 0; y < (int)info_hdr._height; y ++)
		{
			int del3 = 0;
			for (int x = 0; x < (int)info_hdr._width; x ++)
			{
				fread(&c, 1, 3, f);
				del3++;
				int k = (info_hdr._height - y - 1)*StX*4 + x*4;
				_tex[k  ] = c[2];
				_tex[k+1] = c[1];
				_tex[k+2] = c[0];
				_tex[k+3] = 255;
			}
			fread(&c, 1, del3%4, f);
		}
		fclose(f);

		if (strcmp(alpha,"Auto") == 0)
		{
			for (int y = 0; y < (int)info_hdr._height; y ++)
				for (int x = 0; x < (int)info_hdr._width; x ++)
				{
					int k = y*StX*4 + x*4;
					if (_tex[k  ]==0 && _tex[k+1]==0 && _tex[k+2]==0)
						_tex[k+3] = 0;
					else _tex[k+3] = 255;
				}
		}
		else if (strcmp(alpha, "Null") != 0)
		{
			f = fopen(alpha, "rb");
			unsigned char x[100];
			fread(&x, 54, 1, f);
			for (int y = 0; y < (int)info_hdr._height; y ++)
			{
				int del3 = 0;
				for (int x = 0; x < (int)info_hdr._width; x ++)
				{
					fread(&c, 1, 3, f);
					del3++;
					int k = y*StX*4 + x*4;
					_tex[k+3] = c[0];
				}
				fread(&c, 1, del3%4, f);
			}
			fclose(f);
		}

		glGenTextures( 1, &_tex_id[num] );
		//std::cout << _tex_id[num] << "\n";
		glBindTexture( GL_TEXTURE_2D, _tex_id[num] );
		glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, StX, StY, 0, GL_RGBA, GL_UNSIGNED_BYTE, _tex );

		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

		delete [] _tex;

		Message( "GE", (string("Texture ")+string(path)+string(" is loaded.")).c_str() );
	}

	void ZGraphicEngineOpenGL::load_texture_from_png(int num, const char * path)
	{
		std::vector<unsigned char> image;
		unsigned width, height;

		unsigned int error = lodepng::decode(image, width, height, path);

		if (error)
		{
			std::string err = std::string("LodePNG decoder error; cannot load ") + std::string(path);
			Error("GE", err.c_str());
		}

		char ch[200];
		sprintf( ch, "%s is decoded: W=%d H=%d", path, width, height );
		Message( "GE", ch );

		unsigned int StX=1, StY=1;

		while (StX < width) StX*=2;
		while (StY < height) StY*=2;

		_sc_tex_x[num] = (float)(width)/(float)(StX);
		_sc_tex_y[num] = (float)(height)/(float)(StY);

		_sz_tex_x[num] = StX;
		_sz_tex_y[num] = StY;

		unsigned char * _tex = new unsigned char [StX*StY*4];

		int ind = 0;
		for (int y = 0; y < (int)height; y ++)
			for (int x = 0; x < (int)width; x ++)
			{
				int k = y*StX*4 + x*4;
				_tex[k  ] = image[ind  ];
				_tex[k+1] = image[ind+1];
				_tex[k+2] = image[ind+2];
				_tex[k+3] = image[ind+3];
				ind+=4;
			}

		glGenTextures( 1, &_tex_id[num] );
		//std::cout << _tex_id[num] << "\n";
		glBindTexture( GL_TEXTURE_2D, _tex_id[num] );
		glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, StX, StY, 0, GL_RGBA, GL_UNSIGNED_BYTE, _tex );

		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

		delete [] _tex;

		std::string mess = std::string(path) + std::string(" was loaded by LodePNG");
		Message("GE", mess.c_str());
	}

	void ZGraphicEngineOpenGL::begin_scene()
	{
		//#ifndef MESA
		//glDrawBuffer(GL_FRONT_AND_BACK);
		//#endif // !MESA
	}

	void ZGraphicEngineOpenGL::clear_scene( float R, float G, float B )
	{
		glClearColor( R, G, B, 0.0 );
		glClear(GL_COLOR_BUFFER_BIT);
	}

	void ZGraphicEngineOpenGL::render_scene()
	{
		//#ifndef MESA
		//glDrawBuffer(GL_BACK);
		//#endif // !MESA
	}

	void ZGraphicEngineOpenGL::set_texture(int T)
	{
		if (T != _cur_t)
		{
			_cur_t = T;
			glBindTexture( GL_TEXTURE_2D, _tex_id[T] );
		}
	}

	void ZGraphicEngineOpenGL::draw_pict(ge::PICT & pict)
	{
		float rr[4], gg[4], bb[4], aa[4];
		for (int i=0; i<4; i++)
		{
			rr[i] = _r(pict.v[i].clr)/255.f;
			gg[i] = _g(pict.v[i].clr)/255.f;
			bb[i] = _b(pict.v[i].clr)/255.f;
			aa[i] = _a(pict.v[i].clr)/255.f;
		}
		glEnable(GL_TEXTURE_2D);
		glBegin(GL_POLYGON);

		glColor4f( rr[0], gg[0], bb[0], aa[0] );
		glTexCoord2f( pict.v[0].tx * _sc_tex_x[_cur_t], pict.v[0].ty * _sc_tex_y[_cur_t] );
		glVertex2f( pict.v[0].x, _sy - pict.v[0].y );

		glColor4f( rr[1], gg[1], bb[1], aa[1] );
		glTexCoord2f( pict.v[1].tx * _sc_tex_x[_cur_t], pict.v[1].ty * _sc_tex_y[_cur_t] );
		glVertex2f( pict.v[1].x, _sy - pict.v[1].y );

		glColor4f( rr[3], gg[3], bb[3], aa[3] );
		glTexCoord2f( pict.v[3].tx * _sc_tex_x[_cur_t], pict.v[3].ty * _sc_tex_y[_cur_t] );
		glVertex2f( pict.v[3].x, _sy - pict.v[3].y );

		glColor4f( rr[2], gg[2], bb[2], aa[2] );
		glTexCoord2f( pict.v[2].tx * _sc_tex_x[_cur_t], pict.v[2].ty * _sc_tex_y[_cur_t] );
		glVertex2f( pict.v[2].x, _sy - pict.v[2].y );

		glEnd();
		glDisable(GL_TEXTURE_2D);
	}

	void ZGraphicEngineOpenGL::render_to_texture( int T )
	{
		if (_target_tex != -1)
		{
			glBindTexture( GL_TEXTURE_2D, _tex_id[_target_tex] );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
			glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 0, _sy-_sz_tex_y[_target_tex], _sz_tex_x[_target_tex], _sz_tex_y[_target_tex], 0 );
			//glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, 512, 256 ); //_sz_tex_x[_target_tex], _sz_tex_y[_target_tex] );
		}
		_target_tex = T;
	}

	void ZGraphicEngineOpenGL::write(float x, float y, const char *ch, float R, float G, float B, float A)
	{
		//glColor4f( R, G, B, A );
		//y = _sy - y;
		//gl_draw( ch, (int)x-200, (int)y-400, 400, 400, FL_ALIGN_TOP );
		int pre_t = _cur_t;
		set_texture( 4 );

		int ii = 0;
		float yy = y;
		for (int a=0; ; a++)
			if (ch[a]=='\n' || ch[a]==0)
			{
				int sz = a-ii;
				float xx = x - ((sz/2)*7) - (sz&1)*3 - 2.f;
				for (int b=0; b<sz; b++)
				{
					float dx = (float)((unsigned char)ch[ii+b]&15)/16.f;
					float dy = (float)((unsigned char)ch[ii+b]>>4)/16.f;
					float ds = 1.f/16.f;
					draw_pict( ge::PICT( xx, yy, 12.f, 12.f ).set_tex( dx, dy, ds, ds ).mul_to_color( R, G, B, A ) );
					xx += 7.f;
				}
				ii = a+1;
				yy += 14.f;

				if (ch[a]==0) break;
			}

		set_texture( pre_t );
	}
};
