/***************************************************************************
*   Copyright (C) 2004 by Messenger of death                              *
*   riper@fromru.com                                                      *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
*                                                                         *
*   You should have received a copy of the GNU General Public License     *
*   along with this program; if not, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
***************************************************************************/
#include "me_defines.h"
#ifdef WIN32
#include <windows.h>
#endif
#include "me_debug.h"
#include "me_c_opengl_video_surface.h"
#include "me_c_opengl_video_driver.h"
#include "me_c_video.h"
namespace OpenGL
{
#include <GL/gl.h>
}
namespace ME_n_Video
{

	ME_c_OpenGL_Video_Surface::ME_c_OpenGL_Video_Surface()
			: ME_c_Video_Surface()
	{
		ME_c_Video z;
		_surface_ = 0;
		_drv_ = dynamic_cast<ME_c_OpenGL_Video_Driver*>( &*z );
		_need_update_ = true;
		_alpha_ = true;
		_scale_x_ = _scale_y_ = 1.0;
		_rotation_[0]=0;
		_rotation_[1]=0;
		_rotation_[2]=0;

	}

	ME_c_OpenGL_Video_Surface::~ME_c_OpenGL_Video_Surface()
	{
		/// @todo реализовать корректный деструктор
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Surface: destructing" << std::endl;
		if ( _surface_ != 0 ) delete[] _surface_;
		std::vector<ME_c_Video_Surface_Data>::iterator it;
		for ( it = _tex_.begin();it < _tex_.end();++it )
		{
			OpenGL::glDeleteTextures( 1, ( OpenGL::GLuint* ) & ( *it )._tex_id_ );
		}
		std::vector<Uint32>::iterator it1;
		for ( it1 = _list_id_.begin();it1 < _list_id_.end();++it1 )
		{
			OpenGL::glDeleteLists( *it1 ,1 );
		}
	}

	Sint8 ME_c_OpenGL_Video_Surface::_get_bpp() const
	{
		return _bpp_;
	}

	Sint16 ME_c_OpenGL_Video_Surface::_get_width() const
	{
		return _w_;
	}

	Sint16 ME_c_OpenGL_Video_Surface::_get_height() const
	{
		return _h_;
	}

	void* ME_c_OpenGL_Video_Surface::_get_surface_data()
	{
		_need_update_ = true;
		return _surface_;
	}

	void* ME_c_OpenGL_Video_Surface::operator ->()
	{
		_need_update_ = true;
		return _surface_;
	}
	Sint16 ME_c_OpenGL_Video_Surface::_get_line_size() const
	{
		return _w_ * _bpp_;
	}
	Uint32 ME_c_OpenGL_Video_Surface:: _get_Rmask() const
	{
		return _Rmask_;
	}
	Uint32 ME_c_OpenGL_Video_Surface:: _get_Gmask() const
	{
		return _Gmask_;
	}
	Uint32 ME_c_OpenGL_Video_Surface:: _get_Bmask() const
	{
		return _Bmask_;
	}
	Uint32 ME_c_OpenGL_Video_Surface:: _get_Amask() const
	{
		return _Amask_;
	}
	bool ME_c_OpenGL_Video_Surface::_create_( Uint16 w, Uint16 h, OpenGL::GLenum format, OpenGL::GLenum type )
	{
		_need_update_ = true;
		switch ( format )
		{
			case GL_BGR:
			case GL_RGB: _bpp_ = 3;
			break;
			case GL_BGRA:
			case GL_RGBA: _bpp_ = 4;
			break;
			default: return false;
		}
		if ( type != GL_UNSIGNED_INT ) return false;
		_w_ = w;
		_h_ = h;
		_surface_ = new unsigned char[ w * h * _bpp_ ];
		_Rmask_ = 0xff000000;
		_Gmask_ = 0x00ff0000;
		_Bmask_ = 0x0000ff00;
		_Amask_ = 0x000000ff;
		_alpha_ = true;
		return true;
	}

	void ME_c_OpenGL_Video_Surface::_split_texture_( Uint16 x, Uint16 y, Uint16 w, Uint16 h, Uint32 *mas, Uint8 len )
	{
		ME_n_Log::_debug( 1 ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Surface: _sprit_texture_(" << x << " , " << y << " , " << w << " , " << h << " , , " << ( Uint16 ) len << ")" << std::endl;
		//Horiz_split
		Uint16 wn, hn;
		for ( Sint8 a = len - 1;a >= 0;a-- )
		{
			if ( mas[ a ] > w ) continue;
			wn = mas[ a ];
			break;
		}
		ME_n_Log::_debug( 1 ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Surface: _sprit_texture_ - wn=" << wn << std::endl;
		//Vert split
		for ( Sint8 a = len - 1;a >= 0;a-- )
		{
			if ( mas[ a ] > h ) continue;
			hn = mas[ a ];
			break;
		}
		ME_n_Log::_debug( 1 ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Surface: _sprit_texture_ - hn=" << hn << std::endl;
		Uint32 t;
		OpenGL::glGenTextures( 1, ( OpenGL::GLuint* ) & t );
		ME_c_Video_Surface_Data z( x, y, wn, hn, t );
		_tex_.push_back( z );

		Uint8 * buf;
		buf = new Uint8[ wn * hn * _bpp_ ];
		ME_n_Log::_debug( 1 ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Surface: _split_texture_ - generating texture of size : [" << wn << ";" << hn << "] bpp is " << ( Uint16 ) _bpp_ << " Texture ID = " << t << std::endl;
		for ( Sint16 hc = 0;hc < hn;hc++ )
		{

			memmove( buf + ( hc * wn * _bpp_ ), ( Uint8* ) _surface_ + ( ( hc + y ) * _w_ * _bpp_ + x * _bpp_ ), wn * _bpp_ );
		}
		OpenGL::glBindTexture( GL_TEXTURE_2D, t );
		OpenGL::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
		OpenGL::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
		OpenGL::glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
		OpenGL::glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
		OpenGL::glTexImage2D( GL_TEXTURE_2D, 0, 4, wn, hn, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf );
		delete[] buf;


		if ( wn != _w_ -x ) _split_texture_( x + wn, y, _w_ -( x + wn ), _h_ -y, mas, len );
		if ( hn != _h_ -y ) _split_texture_( x, y + hn, wn, _h_ -( y + hn ), mas, len );
	}

	void ME_c_OpenGL_Video_Surface::_update()
	{
		/// @todo реализовать хранения данных о координатах вершин в видеопамяти
		Uint32 st2[ 32 ];
		bool wok = false, hok = false;
		float w, h;
		Sint16 ww, wh;
		ww = _drv_->_w_ ;
		wh = _drv_->_h_ ;
		w = ( float ) _w_ * 2. / ( float ) ww;
		h = ( float ) _h_ * 2. / ( float ) wh;
		Uint32 MaxTexSize, size_num, z;
		OpenGL::glGetIntegerv( GL_MAX_TEXTURE_SIZE, ( OpenGL::GLint* ) & MaxTexSize );

		for ( size_num = 0, z = 1;z < MaxTexSize;size_num++, z <<= 1 )
		{
			st2[ size_num ] = z;
			if ( st2[ size_num ] == _w_ ) wok = true;
			if ( st2[ size_num ] == _h_ ) hok = true;
			if ( wok && hok )
			{
				size_num++;
				break;
			}
		}

		ME_n_Log::_debug( 1 ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Surface: _update() -Num of texture sizes = " << size_num << std::endl;
		_tex_.clear();
		_list_id_.clear();
		_split_texture_( 0, 0, _w_, _h_, st2, size_num );
		std::vector<ME_c_Video_Surface_Data>::iterator it;
		Uint32 lst;
		for ( it = _tex_.begin();it < _tex_.end();++it )
		{
			float x1, y1, x2, y2;

			ME_n_Log::_debug( 1 ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Surface: _update() - ww = " << ww << "; wh= " << wh << std::endl;

			x1 = ( float ) ( *it )._x / ( float ) ww;
			x2 = x1 + ( float ) ( *it )._w / ( float ) ww;
			y1 = ( float ) ( *it )._y / ( float ) wh;
			y2 = y1 + ( float ) ( *it )._h / ( float ) wh;
			ME_n_Log::_debug( 1 ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Surface: _update() - x1 = " << x1 << ";  y1= " << y1 << ";  x2= " << x2 << ";  y2= " << y2 << std::endl;

			lst = OpenGL::glGenLists( 1 );
			_list_id_.push_back( lst );
			OpenGL::glNewList( lst, GL_COMPILE );

			OpenGL::glBindTexture( GL_TEXTURE_2D, ( *it )._tex_id_ );
			ME_n_Log::_debug( 1 ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Surface: _update() - Texture is " << ( *it )._tex_id_ << std::endl;

			OpenGL::glBegin( GL_QUADS );

			OpenGL::glTexCoord2d( 0, 0 );
			OpenGL::glVertex2f( 2. * x1, 2. * y1 );

			OpenGL::glTexCoord2d( 1, 0 );
			OpenGL::glVertex2f( 2. * x2, 2. * y1 );

			OpenGL::glTexCoord2d( 1, 1 );
			OpenGL::glVertex2f( 2. * x2 , 2. * y2 );

			OpenGL::glTexCoord2d( 0, 1 );
			OpenGL::glVertex2f( 2. * x1, 2. * y2 );

			OpenGL::glEnd();
			OpenGL::glEndList();
			_need_update_ = false;
		}
	}
	void ME_c_OpenGL_Video_Surface::_blit_( double x, double y )
	{
		std::vector<Uint32>::iterator it;
		if ( _need_update_ ) _update();
		OpenGL::glPushMatrix();
		OpenGL::glLoadIdentity();
		OpenGL::glTranslatef( x, y, 0 );
		OpenGL::glScalef( _scale_x_, _scale_y_, 1 );
		OpenGL::glRotatef(_rotation_[0],1,0,0);
		OpenGL::glRotatef(_rotation_[1],0,1,0);
		OpenGL::glRotatef(_rotation_[2],0,0,1);
		for ( it = _list_id_.begin();it < _list_id_.end();++it )
		{
			OpenGL::glCallList( *it );
		}
		OpenGL::glPopMatrix();
	}

}

bool ME_n_Video::ME_c_OpenGL_Video_Surface::_get_alpha_status( )
{
	return _alpha_;
}

void ME_n_Video::ME_c_OpenGL_Video_Surface::_set_alpha_status( bool act )
{
	_alpha_ = act;
}

bool ME_n_Video::ME_c_OpenGL_Video_Surface::_set_scale( float x, float y )
{
	_scale_x_ = x;
	_scale_y_ = y;
}

bool ME_n_Video::ME_c_OpenGL_Video_Surface::_set_rotation( float x, float y, float z )
{
	_rotation_[0]=x;
	_rotation_[1]=y;
	_rotation_[2]=z;
}

