/***************************************************************************
*   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_config.h"
#include "me_c_storage.h"
#include "me_c_video.h"
#include "me_c_video_driver_interface.h"
#include "me_c_video_surface.h"
#include "me_c_video_features.h"

#include "me_c_opengl_video_driver.h"
#include "me_c_opengl_video_surface.h"
#include <string>

namespace SDL
{
#include <SDL.h>
}
namespace OpenGL
{
#include <GL/gl.h>
}

namespace ME_n_Video
{
	typedef std::map<std::string, std::map<std::string, ME_c_Video_Surface*>* > _Group_Map_;
	typedef std::map<std::string, ME_c_Video_Surface*>_Surface_Map_;

	ME_c_OpenGL_Video_Driver::ME_c_OpenGL_Video_Driver()
	{
		_driver_name_ = "Magic Engine OpenGL video driver";
	}


	ME_c_OpenGL_Video_Driver::~ME_c_OpenGL_Video_Driver()
	{}

	ME_c_Video_Surface& ME_c_OpenGL_Video_Driver::_get_main_surface()
	{
		//		return _video_surface_;
	}
	void ME_c_OpenGL_Video_Driver::_draw( )
	{
		OpenGL::glFlush();
		SDL::SDL_GL_SwapBuffers( );
		OpenGL::glClear( GL_COLOR_BUFFER_BIT );
	}
	bool ME_c_OpenGL_Video_Driver::_blit(               /* const*/ ME_c_Video_Surface& from, double x, double y, bool rise_exception )
	{
		/// @todo Анализ на ошибку в OpenGL.
		ME_n_Log::_debug( 1 ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: _blit_surface - started" << std::endl;
		
		ME_c_OpenGL_Video_Surface & fr = dynamic_cast<ME_c_OpenGL_Video_Surface&>( from );

		ME_n_Log::_debug( 1 ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: _blit_surface - position is: (" << x << " : " << y << ")" << std::endl;

//		OpenGL::glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
		if(fr._alpha_)OpenGL::glEnable( GL_BLEND );
		else OpenGL::glDisable( GL_BLEND );
		fr._blit_( x, y );
		return true;
	}
	bool ME_c_OpenGL_Video_Driver::_blit(               /* const*/ ME_c_Video_Surface& from, Sint16 x, Sint16 y, bool rise_exception )
	{
		/// @todo Вернуть const ME_c_Video_Surface& from
		float xs, ys, xe, ye;
		xs = ( float ) ( x ) * ( float ) ( 2. / ( float ) _w_ );
		ys = ( float ) ( y ) * ( float ) ( 2. / ( float ) _h_ );

		return _blit( from, xs, ys, rise_exception );
	}
	bool ME_c_OpenGL_Video_Driver::_blit( const std::string &from, Sint16 x, Sint16 y, bool rise_exception )
	{
		try
		{
			_blit( _get_surface( _cur_group_, from ), x, y, true );

		}
		catch ( ME_Exception ex )
		{
			if ( !rise_exception ) return false;
			throw ex;
		}
	}
	bool ME_c_OpenGL_Video_Driver::_blit( const std::string &from, double x, double y, bool rise_exception )
	{
		try
		{
			_blit( _get_surface( _cur_group_, from ), x, y, true );

		}
		catch ( ME_Exception ex )
		{
			if ( !rise_exception ) return false;
			throw ex;
		}
	}

	bool ME_c_OpenGL_Video_Driver::_free_surface( ME_c_Video_Surface * surf )
{}

	bool ME_c_OpenGL_Video_Driver::_free_surface( std::string name )
	{}

	bool ME_c_OpenGL_Video_Driver::_set_group( std::string name )
	{}

	const std::string ME_c_OpenGL_Video_Driver::_get_driver_name()
	{
		return _driver_name_;
	}

	ME_c_Video_Driver_Interface& ME_c_OpenGL_Video_Driver::operator ( ) ( std::string name )
	{}

	ME_c_Video_Surface& ME_c_OpenGL_Video_Driver::_create_surface( double xs, double ys )
	{}
	ME_c_Video_Surface& ME_c_OpenGL_Video_Driver::_create_surface( Sint16 xs, Sint16 ys )
	{
		//				_create_surface(xs,ys,
	}

	ME_c_Video_Surface& ME_c_OpenGL_Video_Driver::_create_surface( double xs, double ys, std::string name, bool rewrite, bool rise_exception )
	{}
	ME_c_Video_Surface& ME_c_OpenGL_Video_Driver::_create_surface( Sint16 xs, Sint16 ys, std::string name, bool rewrite, bool rise_exception )
	{
		return _create_surface( xs, ys, name, rewrite, _cur_group_, rise_exception );
	}

	ME_c_Video_Surface& ME_c_OpenGL_Video_Driver::_create_surface( double xs, double ys, std::string name, bool rewrite, std::string group_name, bool rise_exception )
	{}
	ME_c_Video_Surface& ME_c_OpenGL_Video_Driver::_create_surface( Sint16 xs, Sint16 ys, std::string name, bool rewrite, std::string group_name, bool rise_exception )
	{
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: _create_surface( " << xs << ", " << ys << "," << name << "," << rewrite << "," << group_name << " ) started" << std::endl;


		ME_c_OpenGL_Video_Surface *s;
		s = new ME_c_OpenGL_Video_Surface;

		if ( !s->_create_( xs, ys, GL_RGBA, GL_UNSIGNED_INT) )
			ME_n_Log::_debug( ME_n_Log::ME_DEBUG_ERROR ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: _create_surface( " << xs << ", " << ys << "," << name << "," << rewrite << "," << group_name << " ) - " << "cannot crate surface" << std::endl;

		if ( _surfaces_.count( group_name ) == 0 )
		{
			ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: _create_surface - adding group \""<<group_name<<"\"" << std::endl;
			
			_surfaces_.insert( std::make_pair( group_name, new std::map < std::string, ME_c_Video_Surface* > ) );
			
			ME_n_Log::_debug(ME_n_Log::ME_DEBUG_INFO)<<__FILE__<<":"<<__LINE__<<"- ME_c_OpenGL_Video_Driver: _create_surface - currently we have group \""<<group_name<<"\" at least "<<_surfaces_.count( group_name )<<std::endl;
		}
		else
		{
			ME_n_Log::_debug( ME_n_Log::ME_DEBUG_ERROR ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: _create_surface( " << xs << ", " << ys << "," << name << "," << rewrite << "," << group_name << " ) - group already exist" << std::endl;
		}

		_Surface_Map_ &smap = *_surfaces_[ group_name ];
		if ( smap.count( name ) != 0 )
		{
			ME_n_Log::_debug( ME_n_Log::ME_DEBUG_ERROR ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: _create_surface( " << xs << ", " << ys << "," << name << "," << rewrite << "," << group_name << " ) - surface exist" << std::endl;
			if ( !rewrite )
			{
				_Surface_Map_::iterator pos;
				if ( !rise_exception ) return * smap[ name ];
				else throw ME_Exception( ME_VIDEO_SURFACE_EXIST, "Surface already exist" );
			}
		}

		smap.insert( std::make_pair( name, s ) );
		return *smap[ name ];
	}




	ME_c_Video_Surface& ME_c_OpenGL_Video_Driver::_get_surface( std::string grp, std::string name )
	{
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: _get_surface( " << grp << "," << name << ") - started" << std::endl;

		if ( _surfaces_.count( grp ) == 0 ) throw ME_Exception( ME_VIDEO_NO_GROUP, "Group don`t exist" );
		_Surface_Map_ &smap = *_surfaces_[ grp ];
		if ( smap.count( name ) == 0 ) throw ME_Exception( ME_VIDEO_NO_SURFACE, "Surface don`t exist" );
		return *smap[ name ];
	}

	ME_c_Video_Surface& ME_c_OpenGL_Video_Driver::_insert_surface( ME_c_Video_Surface * surf, std::string name, bool rewrite, bool rise_exception )
{}

	ME_c_Video_Surface& ME_c_OpenGL_Video_Driver::_insert_surface( ME_c_Video_Surface * surf, std::string name, bool rewrite, std::string grp, bool rise_exception )
	{}

	ME_c_Video_Surface& ME_c_OpenGL_Video_Driver::operator [ ] ( std::string name )
	{
		return _get_surface( _cur_group_, name );
	}

	std::string ME_c_OpenGL_Video_Driver::_allocate_surface( double xs, double ys )
	{}
	std::string ME_c_OpenGL_Video_Driver::_allocate_surface( Sint16 xs, Sint16 ys )
	{}

	std::string ME_c_OpenGL_Video_Driver::_insert_surface( ME_c_Video_Surface * surf )
	{}


	/*!
	    \fn ME_n_Video::ME_c_OpenGL_Video_Driver::_init()
	 */
	/**
	Выполняет инициализацию драйвера OpenGL
	*/
	bool ME_c_OpenGL_Video_Driver::_init()
	{
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: _init " << std::endl;

		ME_n_Storage::ME_c_Storage &CFG = ME_n_Storage::_CFG();

		double RGamma, GGamma, BGamma;
		Uint32 sdl_flags = 0;

		//Чтение конфигурации
		sdl_flags |= ( CFG._get_value( "Video->FullScreen", ( Uint32 ) 0 ) == 1 ) ? SDL_FULLSCREEN : 0;
		sdl_flags |= ( CFG._get_value( "Video->OpenGL->NoFrame", ( Uint32 ) 0 ) == 1 ) ? SDL_NOFRAME : 0;
		sdl_flags |= ( CFG._get_value( "Video->OpenGL->Resizable", ( Uint32 ) 0 ) == 1 ) ? SDL_RESIZABLE : 0;

		_bpp_ = CFG._get_value( "Video->Bpp", ( Uint32 ) 15 );
		_h_ = CFG._get_value( "Video->Height", ( Uint32 ) 600 );
		_w_ = CFG._get_value( "Video->Width", ( Uint32 ) 800 );
		RGamma = CFG._get_value( "Video->Gamma->RGamma", ( double ) 1.0 );
		GGamma = CFG._get_value( "Video->Gamma->GGamma", ( double ) 1.0 );
		BGamma = CFG._get_value( "Video->Gamma->BGamma", ( double ) 1.0 );
		_RSize_ = CFG._get_value( "Video->OpenGL->RedSize", ( Uint32 ) 5 );
		_GSize_ = CFG._get_value( "Video->OpenGL->GreenSize", ( Uint32 ) 5 );
		_BSize_ = CFG._get_value( "Video->OpenGL->BlueSize", ( Uint32 ) 5 );
		_ASize_ = CFG._get_value( "Video->OpenGL->AlphaSize", ( Uint32 ) 5 );
		_depth_ = CFG._get_value( "Video->OpenGL->DepthBufferSize", ( Uint32 ) 16 );


		if ( ( SDL::SDL_Init( SDL_INIT_VIDEO ) == -1 ) )
		{
			ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: Failed to initialize SDL." << SDL::SDL_GetError() << std::endl;
		}

		SDL::SDL_GL_SetAttribute( SDL::SDL_GL_RED_SIZE, _RSize_ );
		SDL::SDL_GL_SetAttribute( SDL::SDL_GL_GREEN_SIZE, _GSize_ );
		SDL::SDL_GL_SetAttribute( SDL::SDL_GL_BLUE_SIZE, _BSize_ );
		SDL::SDL_GL_SetAttribute( SDL::SDL_GL_ALPHA_SIZE, _ASize_ );
		SDL::SDL_GL_SetAttribute( SDL::SDL_GL_DEPTH_SIZE, _depth_ );
		SDL::SDL_GL_SetAttribute( SDL::SDL_GL_DOUBLEBUFFER, CFG._get_value( "Video->OpenGL->DoubleBuffer", ( Uint32 ) 1 ) );

		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: SDL_GL_SetAttribute ... ok" << std::endl;

		const SDL::SDL_VideoInfo*    info = SDL::SDL_GetVideoInfo( );

		if ( ( SDL::SDL_SetVideoMode( _w_, _h_, info->vfmt->BitsPerPixel, sdl_flags | SDL_OPENGL ) ) == 0 )
		{
			ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: Failed to initialize SDL window." << SDL::SDL_GetError() << std::endl;
			return false;
		}
		SDL::SDL_WM_SetCaption( CFG._get_value( "System->Caption", ( std::string ) "MagicEngine-2.0.0" ).c_str(), NULL );
		//Чтение конфигурации окончено


		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver : OpenGL info:" << std::endl <<
		"OpenGL version: " << OpenGL::glGetString( GL_VERSION ) << std::endl <<
		"Vendor:: " << OpenGL::glGetString( GL_VENDOR ) << std::endl <<
		"Supported Extesions: " << OpenGL::glGetString( GL_EXTENSIONS ) << std::endl <<
		//		"Shading Language Version: " << OpenGL::glGetString( GL_SHADING_LANGUAGE_VERSION ) << std::endl <<
		"Renderer info: " << OpenGL::glGetString( GL_RENDERER ) << std::endl;

		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: Video->FullScreen=" << ( bool ) ( sdl_flags | SDL_FULLSCREEN ) << std::endl;
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: Video->Bpp=" << ( Uint16 ) _bpp_ << std::endl;
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: Video->Width=" << _w_ << std::endl;
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: Video->Height=" << _h_ << std::endl;
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: Video->Gamma->RGamma=" << RGamma << std::endl;
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: Video->Gamma->GGamma=" << GGamma << std::endl;
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: Video->Gamma->BGamma=" << BGamma << std::endl;

		OpenGL::GLint texts;
		OpenGL::glGetIntegerv( GL_MAX_TEXTURE_SIZE, &texts );
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: MaxtextureSize=" << texts << std::endl;

		SDL::SDL_SetGamma( RGamma, GGamma, BGamma );
		
		OpenGL::glEnable( GL_TEXTURE_2D );

		OpenGL::glViewport ( 0, 0, _w_, _h_ );
		OpenGL::glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		OpenGL::glClearColor( 0, 0, 0, 0 );
		OpenGL::glClear( GL_COLOR_BUFFER_BIT );

		return true;
	}
	void ME_c_OpenGL_Video_Driver::_deinit()
	{
		SDL::SDL_Quit();
	}

	ME_c_Video_Driver_Interface* _create_video_driver()
	{
		return new ME_c_OpenGL_Video_Driver;
	}
	const ME_c_Video_Features & ME_c_OpenGL_Video_Driver::_get_features( )
{}}

bool ME_n_Video::ME_c_OpenGL_Video_Driver::_free_surface( std::string group, std::string name )
{
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: _free_surface( " << group << "," << name << ") - started" << std::endl;

		if ( _surfaces_.count( group ) == 0 ) return false;
		_Surface_Map_ &smap = *_surfaces_[ group ];
		if ( smap.count( name ) == 0 ) return false;
		delete smap[ name ];
		smap.erase(smap.find(name));
		if (smap.empty())
		{
		ME_n_Log::_debug( ME_n_Log::ME_DEBUG_INFO ) << __FILE__ << ":" << __LINE__ << " - ME_c_OpenGL_Video_Driver: _free_surface - deleteing emty group:"<<group << std::endl;
		_surfaces_.erase(_surfaces_.find(group));
		}
		return true;
}

