#include "ModelViewerApp.h"
extern KODIAK_DEBUG_STREAM KODIAK_DEBUG_COUT;

const string ModelViewerApp::LIGHT_0="light_botom";
const string ModelViewerApp::LIGHT_1="light_top";

bool ModelViewerApp::isInitialized(void) 
{ 
	return this->initialized; 
}
void ModelViewerApp::mouseWheelEvent( int delta )
{
	this->mouse.wheel = delta;
}

void ModelViewerApp::setResolution( unsigned int sizeX, unsigned int sizeY )
{
	this->windowRes[0] = sizeX;
	this->windowRes[1] = sizeY;

	if ( this->render!= NULL )
	{
		this->render->setViewport(0,0, this->windowRes[0], this->windowRes[1] );

		this->render->setProjection( float( this->windowRes[0] ) / float ( this->windowRes[1] ), 
									this->sharedData.externalParams.projection.zNear, this->sharedData.externalParams.projection.zFar,
									this->sharedData.externalParams.projection.FOV );
	}
}

void ModelViewerApp::mouseMoveEvent( unsigned int posX, unsigned int posY )
{
	this->mouse.setPos( posX, posY );

	if ( this->mouse.pressedButtons[ Mouse::LEFT ] )
	{
		QCursor cursor = this->myWidget->cursor();
		QPoint widgetGlobalPos = this->myWidget->mapToGlobal( this->myWidget->pos() );
		cursor.setPos(  widgetGlobalPos.x() + this->myWidget->width() / 2, widgetGlobalPos.y()  + this->myWidget->height() / 2 );
	}
}

void ModelViewerApp::mouseReleaseEvent( unsigned int mouseButton )
{
	this->newClick = false;
	this->mouse.pressedButtons[ mouseButton ] = false;

	if ( mouseButton == Mouse::LEFT )
	{
		//set parent cursor to restore the previous cursor
		this->myWidget->setCursor( this->myWidget->parentWidget()->cursor() );
	}
}

void ModelViewerApp::mousePressEvent( unsigned int mouseButton )
{
	this->mouse.pressedButtons[ mouseButton ] = true;

	if ( mouseButton == Mouse::LEFT )
	{
		//hide cursor while cursor is centered
		this->myWidget->setCursor( QCursor( Qt::BlankCursor ) );

		if ( this->newClick == false  )	
		{
			this->newClick = true;
			this->mouse.setPos( this->myWidget->width()/2, this->myWidget->height()/2 );
			this->mouse.clearDiff();
		}
	

		QCursor cursor = this->myWidget->cursor();
		QPoint widgetGlobalPos = this->myWidget->mapToGlobal( this->myWidget->pos() );
		cursor.setPos(  widgetGlobalPos.x() + this->myWidget->width() / 2, widgetGlobalPos.y() + this->myWidget->height() / 2 );
	}
}

void ModelViewerApp::keyPressEvent(unsigned int key)
{
	this->keyboard.pressKey( key );
}

void ModelViewerApp::keyReleaseEvent(unsigned int key)
{
	this->keyboard.releaseKey( key );
}

void ModelViewerApp::onOpenGLInit(void)	//init is called from GLWidget!!!
{
	if ( this->initialized ) return;
	this->sharedData.init();

	CGprogram vs, fs;
	if ( this->sharedData.shaderManager != NULL )
	{
		vs = this->sharedData.shaderManager->getShader("GLphongLightingVs");
		fs = this->sharedData.shaderManager->getShader("GLphongLightingFs");
	}
	else
	{
		KODIAK_WARNING<<"CgShaderManager is not initialized!";
	}



	this->render = new SoftProjRender( *this->context, vs, fs);
	KODIAK_DEBUG_COUT<<KODIAK_ENDL<<"ModelViewerApp resolution: "<<this->windowRes[0]<<", "<<this->windowRes[1]<<KODIAK_ENDL;

	this->render->setViewport(0,0, this->windowRes[0], this->windowRes[1] );
	this->render->setProjection( float( this->windowRes[0] ) / float ( this->windowRes[1] ), this->sharedData.externalParams.projection.zNear, this->sharedData.externalParams.projection.zFar, this->sharedData.externalParams.projection.FOV );
	
	this->initItemViewer();
	this->setModel( SHIP_TYPE_0 );
	
	this->initialized = true;
}

ModelViewerApp::ModelViewerApp( const string appName, Context &context, OpenGlSharedData &sharedData ): AbstractAPP( appName, context ), sharedData( sharedData)
{
	this->itemViewer = NULL;
	this->render = NULL;

	//set some default window res
	this->windowRes[0] = 640;
	this->windowRes[1] = 480;

	this->initialized = false;

}
void ModelViewerApp::destroyItemViewer(void)
{
	if ( this->itemViewer != NULL )
	{
		delete this->itemViewer;
		this->itemViewer = NULL;
	}

	if ( this->render != NULL )
	{
		delete this->render;
		this->render = NULL;
	}
}
void ModelViewerApp::initItemViewer(void)
{
	assert( this->itemViewer==NULL && "Memory leak, please release old resources before new alocation!"  );
	this->itemViewer = new ItemViewer( *this->render );

	if ( this->itemViewer == NULL )
	{
		KODIAK_ERROR<< "Memory allocation failed, itemViewer cannot be allocated!";
	}

	OmniLightPhong omni;
	omni.attenuationExp=1.0;
	omni.intensity=10.0;

	omni.position = MAKE_POINT<float>( 0,-20,0 );
	omni.color[0] = 5.0;
	omni.color[1] = 5.0;
	omni.color[2] = 5.0;
	this->itemViewer->lights.createOmniLight( LIGHT_0, omni );

	omni.position = MAKE_POINT<float>( 0,20,0 );
	omni.color[0] = 5.0;
	omni.color[1] = 5.0;
	omni.color[2] = 5.0;
	this->itemViewer->lights.createOmniLight( LIGHT_1, omni );
}
void ModelViewerApp::setModel( const string &modelName )
{
	if ( ! this->initialized ) 
	{
		KODIAK_DEBUG_COUT<<KODIAK_ENDL<<"ModelViewerApp cannot setModel, is not initialized -> ignored"<<KODIAK_ENDL;
		return;
	}

	//delete old redered model
	this->itemViewer->models.clear();

	//search for new model to render in database
	std::map<string, Model* >::iterator it = this->sharedData.modelManager.find( modelName );
	if ( it ==  this->sharedData.modelManager.end() )
	{
		KODIAK_WARNING<<"Model with given name was not found. Model name:"+modelName;
		return;
	}

	this->itemViewer->models.push_back( *it->second );
}
ModelViewerApp::~ModelViewerApp(void)
{
	this->destroyItemViewer();
}
void ModelViewerApp::moveLights( float dt)
{
	if ( ! this->initialized ) 
	{
		KODIAK_DEBUG_COUT<<KODIAK_ENDL<<"ModelViewerApp cannot moveLights, is not initialized -> ignored"<<KODIAK_ENDL;
		return;
	}

	Point_3<float> pivot(0,0,0);
	Vector_3<float> axis1(0.5,0.5,0);
	Vector_3<float> axis2(0,0.5,0.5);

	this->itemViewer->lights.getOmniLight( LIGHT_0 )->position.rotate_point( pivot, axis1, dt );
	this->itemViewer->lights.getOmniLight( LIGHT_1 )->position.rotate_point( pivot, axis2, dt );
}
void ModelViewerApp::simulate( float dt )
{
	if ( ! this->initialized ) 
	{
		KODIAK_WARNING<<KODIAK_ENDL<<"ModelViewerApp cannot simulate, is not initialized -> ignored"<<KODIAK_ENDL;
		return;
	}

	this->moveLights( 1.0 );
	this->itemViewer->handleKeyboard( this->keyboard );
	this->itemViewer->handleMouse( this->mouse );
	this->mouse.clearDiff();
	this->mouse.wheel = 0;
}
void ModelViewerApp::run(void)
{
	if ( ! this->initialized ) 
	{
		KODIAK_WARNING<<KODIAK_ENDL<<"ModelViewerApp cannot render, is not initialized -> ignored"<<KODIAK_ENDL;
		return;
	}

	if ( this->sharedData.shaderManager == NULL )
	{
		KODIAK_WARNING<<"ModelViewerApp: sharedData.shaderManager == NULL -> ignored";
		return;
	}
	
	if ( this->render->isFragmentShaderValid() && this->render->isVertexShaderValid() )
	{
		this->sharedData.shaderManager->bind();
			this->itemViewer->render();
		this->sharedData.shaderManager->unbind();
	}
	else
	{
		this->sharedData.shaderManager->unbind();
		this->itemViewer->render();
	}
}
