#include "MainWindow.h"
#include "./softProj/gameApp/ModelViewerApp.h"
#include "./softProj/gameApp/MapViewerApp.h"
#include "./softProj/gameApp/PlanetViewerApp.h"
#include "console\InputRegister.h"
#include "softProj\clientServer\commandClient.h"
#include "softProj\AttackWidget\AttackWidget.h"
#include "./qtLoop/AutoLoop.h"
#include <qicon.h>
#include "softProj\Render\oglSharedData.h"
#include "windows.h"
#include "softProj\clientServer\ClientSynchronizer.h"
#include "softProj\HangarWidget\HangarUI.h"
#include "texture\TextureComposer.h"
#include "mathParser\mathParser.h"
#include "mathParser\mathStringParser.h"
#include "mathParser\textureOperationParser.h"
#include "graphicMapper.h"
#include "coordConvertor.h"
#include "gameSettings.h"

#include "streams\ErrorStream.h"
extern KODIAK_DEBUG_STREAM KODIAK_DEBUG_COUT;

extern const unsigned int RES_X;
extern const unsigned int RES_Y;

void initModels(void);

ScienceTreeWidget * initTree(QWidget * parentWidget);

ClientSynchronizer * clientSynchronizer = NULL;
MainWindow * mainWindow = NULL;


int loopInterval; //will be read from env file




//path to Qt resource file
const QString resourceFile( "clientapplication.rcc" );

void updateShips(void);

void newShipDestinationPosCallbackMacro( Point_3<double> newDstPos )
{
	unsigned long activeShipId = 0;
	bool ok = mainWindow->otherviewLogic->getShipId( mainWindow->otherviewLogic->getActiveIdx(), activeShipId );

	if ( !ok )
	{
		KODIAK_DEBUG_COUT<<"newShipDestinationPosCallbackMacro no active ship ->ignored"<<KODIAK_ENDL;
		return;
	}



	double serverPosX = GameConstants::MACRO_COORD_CONVERTOR.convertClientToServerValue( newDstPos.x() - GameConstants::MACRO_COORD_CONVERTOR.getCenterOffset().x() );
	double serverPosY = GameConstants::MACRO_COORD_CONVERTOR.convertClientToServerValue( newDstPos.z() - GameConstants::MACRO_COORD_CONVERTOR.getCenterOffset().z()  );

	bool useWarp = false;

	if ( mainWindow->otherviewLogic->useWarp )
	{
		mainWindow->navigationWarpSlot( activeShipId, serverPosX, serverPosY );
	}
	else
	{
		mainWindow->navigationMoveToSlot( activeShipId, serverPosX, serverPosY, mainWindow->otherviewLogic->ignationTimeSec );
	}

	/*
	navigationWarpSlot

	*/
	KODIAK_DEBUG_COUT << "Zmena kurzu ShipId:"<<activeShipId<<"	;	cilova pozice:[ " << serverPosX << ", "<< serverPosY  << " ]"  <<KODIAK_ENDL;
}

void newShipDestinationPosCallbackMicro( Point_3<double> newDstPos )
{
	unsigned long activeShipId = 0;
	bool ok = mainWindow->otherviewLogic->getShipId( mainWindow->otherviewLogic->getActiveIdx(), activeShipId );

	if ( !ok )
	{
		KODIAK_DEBUG_COUT<<"newShipDestinationPosCallbackMicro no active ship ->ignored"<<KODIAK_ENDL;
		return;
	}

	Point_3<double> dstPoint( newDstPos.x(), newDstPos.y(), newDstPos.z() );
	SpaceShip * shipPtr = mainWindow->mapViewerApp->getGalaxyPart()->findShip( activeShipId );
	Point_3<double> dstPos = GameConstants::MICRO_COORD_CONVERTOR.convertClientToServerPoint(  newDstPos );

	double serverPosX = dstPos.x();
	double serverPosY = dstPos.z();

	if ( mainWindow->otherviewLogic->useWarp )
	{
		mainWindow->navigationWarpSlot( activeShipId, serverPosX, serverPosY );
	}
	else
	{
		//cout<<"MOVE TO: "<<serverPosX<<", "<<serverPosY<<endl;
		//system("pause");
		mainWindow->navigationMoveToSlot( activeShipId, serverPosX, serverPosY, mainWindow->otherviewLogic->ignationTimeSec );
	}

	mainWindow->updateSpaceMap();

	KODIAK_DEBUG_COUT << "Zmena kurzu ShipId:"<<activeShipId<<"	;	cilova pozice:[ " << long long(serverPosX) << ", "<< long long(serverPosY)  << " ]"  <<KODIAK_ENDL;

	//cout<< "Zmena kurzu ShipId:"<<activeShipId<<"	;	cilova pozice:[ " << serverPosX << ", "<< serverPosY  << " ]"  <<KODIAK_ENDL;
	//system("pause");
}

void newSelectionCallback( unsigned int idx )
{
	if (  mainWindow == NULL ||  mainWindow->mapViewerApp == NULL || !clientSynchronizer->mapViewer.isInitialized()  || mainWindow->mapViewerApp->getGalaxyPart() == NULL )
	{
		KODIAK_DEBUG_COUT<<"newSelectionCallback() called but cannot be proccesed";
		return;
	}

	unsigned long activeShipId = 0;
	mainWindow->otherviewLogic->getShipId( mainWindow->otherviewLogic->getActiveIdx(), activeShipId );
	mainWindow->setSelectedShipSlot( activeShipId );
	mainWindow->mapViewerApp->setActiveShipId( activeShipId );
	//mainWindow->mapViewerApp->removeSpaceMapObjectsOlderThan( 0 );
	//mainWindow->mapViewerApp->removeRadarObjectsOlderThan( 0 );
	mainWindow->updateSpaceMap();

	mainWindow->newShipSelected = true;
}

void newMicroMacroCallback( void)
{
	mainWindow->mapViewerApp->setMicroMapMode( ! mainWindow->mapViewerApp->isMicroMapMode() );
}
void newRadarCallback( void )
{
	mainWindow->mapViewerApp->sendRadarWave();
}
void processServerMessages(void) 
{
	//server messages cannot be ignored!!!
	CommandClientHolder::getCommandClient()->processIncomingMessages();
}

void simulate(void)
{
	static int skipCount = 0;

	++skipCount;

	if ( !QApplication::instance()->hasPendingEvents() || skipCount > GameConstants::MAX_SIMULATION_SKIP_COUNT )
	{
		skipCount = 0;
		static DWORD lastTime =  GetTickCount();
		DWORD currentTime = GetTickCount();
		DWORD timeDiff = currentTime - lastTime;
		lastTime = currentTime;
		float dt = timeDiff / 1000.0; //convert to from miliseconds to seconds
		mainWindow->simulateSlot( dt * GameConstants::SERVER_TIME_SPEED_UP );
	}
}

void updateMainWindow()
{
	static clock_t start = clock();
	clock_t now = clock();

	if (now - start > 500)
	{
		start = clock();		
		mainWindow->notifyUpdate();
	}
}

void removeOldSpaceAndRadarMapData(void)
{
	if ( !mainWindow->isInMainState( SPACE_MAP ) )
	{
		return;
	}
	
	static clock_t start = clock();
	clock_t now = clock();

	const unsigned int TIME_LIMIT = 20000; // every 20sec
	if (now - start > TIME_LIMIT) 
	{
		start = clock();		
		//remove objects that were not updated from server for long time
		mainWindow->mapViewerApp->removeRadarObjectsOlderThan( TIME_LIMIT, true, true );
		mainWindow->mapViewerApp->removeSpaceMapObjectsOlderThan( TIME_LIMIT, true, true );
	}
}

void updateShips(void)
{
	static int skipCount = 0;
	++skipCount;

	if ( skipCount > GameConstants::MAX_UPDATE_SHIPS_SKIP_COUNT )
	{
		skipCount = 0;
		if ( mainWindow->isInMainState( SPACE_MAP ) )
		{
			/*
			//BUG: server is not sending fortredUpdateReqest if ship was in range and now is out of sensor range
			mainWindow->mapViewerApp->removeRadarObjectsOlderThan( 10 );
			mainWindow->mapViewerApp->removeSpaceMapObjectsOlderThan( 10 );
			*/
			//TODO, use it until server is not working properly


			mainWindow->updateSpaceMap();
		}
	}
}

void render(void)
{
	static int skipCount = 0;
	++skipCount;

	if (  ! QApplication::instance()->hasPendingEvents() || skipCount > GameConstants::MAX_RENDER_SKIP_COUNT )
	{
		skipCount = 0;
		mainWindow->renderSlot();
	}
}

void processApplication( void )
{
	QApplication::instance()->processEvents( QEventLoop::AllEvents );
}

void onGLInit(void) 
{
	static bool initialized = false;

	if ( ! clientSynchronizer->mapViewer.isInitialized() || initialized ) return;


	mainWindow->mapViewerApp->setOnShipNewDestinationPosCallbackFce( newShipDestinationPosCallbackMacro, newShipDestinationPosCallbackMicro );
	initialized = true;

}

int main(int argc, char * argv[])
{
	if ( AllocConsole() == 0 )
	{
		::MessageBox( 0, L"Can't allocate console!\n", L"KODIAK",  MB_OK );
		return FALSE;
	}
	freopen( "CON", "w", stderr);
	freopen( "CON", "w", stdout);
	ios::sync_with_stdio();


	KODIAK_PROGRESS<<"Starting...";

	if ( argc< 2 )
	{
		KODIAK_ERROR<<"Not enough console input parameters, .env path required!";
	}

	KODIAK_PROGRESS<<"Initializing QApplication...";
	QApplication application(argc, argv);

	
	KODIAK_PROGRESS<<"Initializing Qt resource file...";
	QString applicationPath = QCoreApplication::applicationDirPath();

	QString resourcePath = applicationPath+"/"+resourceFile;
	bool resourcesOk = QResource::registerResource( resourcePath );
	
	if ( resourcesOk )
	{
		KODIAK_PROGRESS<<"Qt resource file was succesfully initialized";
	}
	else
	{
		KODIAK_WARNING<<"Qt resource file not found. Check the resource path:"+ resourcePath.toStdString();
	}

	try
	{
		KODIAK_PROGRESS<<"Initializing Environment file...";
		Environment environment;
		InputSpace::registerALL( environment );
		if( environment.init(argc, argv) )
		{
			KODIAK_ERROR<< "Environment file currupted - Environment::init() failed";
		}

		KODIAK_PROGRESS<<"Initializing MainWindow...";
		mainWindow = new MainWindow( argc, argv, environment );


		mainWindow->loadConnections("..\\connections.txt");

		// set application cursor
		QString picturePath = applicationPath + "/" + QString::fromUtf8("../images/cursor/normalCur.png");
		QPixmap pixmap(picturePath.toUtf8().data());
		QCursor cursor( pixmap, 0,0 );
		mainWindow->setCursor( cursor );


		mainWindow->otherviewLogic->newSelectionCallback = newSelectionCallback;
		mainWindow->otherviewLogic->newMacroMicroCallback = newMicroMacroCallback;
		mainWindow->otherviewLogic->newRadarCallback = newRadarCallback;

		KODIAK_PROGRESS<<"Initializing ClientSynchronizer...";
		clientSynchronizer = new ClientSynchronizer( *mainWindow->modelViewerApp, *mainWindow->mapViewerApp, *mainWindow->planetViewerApp, *mainWindow->otherviewLogic );
		mainWindow->synchronizer_ = clientSynchronizer;

		KODIAK_PROGRESS<<"Initializing SocketClient...";
		ASocketClient * client = new SocketClient();

		KODIAK_PROGRESS<<"Initializing CommandClient...";
		CommandClient * command = new CommandClient(client, mainWindow);

		// ------------------------------------------------------

		mainWindow->setCommandClient(command);
		CommandClientHolder::setCommandClient(mainWindow->getCommandClient());
		mainWindow->getCommandClient()->setTimeLimitForProcessing(500);	
		
		if (  environment.getBool("initGraphicOnStart") )
		{
			mainWindow->initOpenGl();	// load all openGL data now
		}

		// ------------------------------------------------------

		KODIAK_PROGRESS<<"Connecting to server...";
		if (argc >= 6) {
			unsigned long port = ValueReader::string2ulong(argv[6]);

			LoginDetails loginDetails;

			loginDetails.name = argv[3];
			loginDetails.password = argv[4];
			loginDetails.server = argv[5];
			loginDetails.port = port;

			mainWindow->ui_.loggedWidget->setLogin(loginDetails);

			if (client->connectToServer(argv[5], port)) {
				KODIAK_PROGRESS<<"Connection succesful";
				command->login(loginDetails);
			}
			else {
				KODIAK_DEBUG_COUT<<"Connection failed!";		
			}
		} 

		// ----------------------------------------------------
		if ( environment.getBool("fullScreen") )
		{
			KODIAK_PROGRESS<<"Initialising fullscreen application mode...";
			mainWindow->ui_.windowFrame->move( QApplication::desktop()->screen()->rect().center() - mainWindow->ui_.windowFrame->rect().center() );
			mainWindow->ui_.windowFrame->setFixedSize( mainWindow->ui_.windowFrame->width(), mainWindow->ui_.windowFrame->height() );
			mainWindow->setFixedSize( QApplication::desktop()->screen()->rect().width(), QApplication::desktop()->screen()->rect().height() );
			mainWindow->showFullScreen();
		}
		else
		{
			mainWindow->show();
		}

		KODIAK_PROGRESS << "Initializing AutoLoop...";
		Loop autoLoop;
		loopInterval = environment.getInt("maxGameLoopInterval");
		autoLoop.start( loopInterval );
		autoLoop.callBackFuncArray.push_back(&processServerMessages);
		autoLoop.callBackFuncArray.push_back(&simulate);
		autoLoop.callBackFuncArray.push_back(&render);
		autoLoop.callBackFuncArray.push_back(&onGLInit);
		autoLoop.callBackFuncArray.push_back(&processApplication);
		autoLoop.callBackFuncArray.push_back(&updateShips);
		autoLoop.callBackFuncArray.push_back(&updateMainWindow);
		autoLoop.callBackFuncArray.push_back(&removeOldSpaceAndRadarMapData);	
		
		KODIAK_PROGRESS << "Running application...";

		if ( environment.getBool("hideConsole") )
		{
			FreeConsole();
		}


		int returnValue = application.exec();
		

		KODIAK_PROGRESS << "Dealocating resources application...";
		delete clientSynchronizer;
		delete mainWindow;

		KODIAK_PROGRESS << "Terminating application...";
		return returnValue;
	}
	catch ( exception &e )
	{
		KODIAK_ERROR<< "Client failed. Reason:" + string(e.what());
		throw e;
		return -1;
	}

}
