
#include <sstream>

#include "context.h"
#include "log.h"
#include "time_mapping.h"

#ifdef _DEBUG
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
#define new DEBUG_NEW
#endif
#endif

/* ze zacatku bude jeden kontext, v pripade potreby se to rozsiri na dynamickou spravu kontextu */

context mainContext;
global_context gctx;

context* actx = &mainContext;

static const dv_time :: datetime sync_interval = (double)dv_time :: datetime :: minute() * (double)5;

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

context* getContextById(unsigned long contextID)
{
    return &mainContext;
}

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


unsigned long getActualContextID(void)
{
    return 0;
}

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

void setContextById(unsigned long contextID)
{
    // not implemented now
}

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

void global_context :: closeClient(id clientID)
{
    srv.closeClient(clientID);
}

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

void global_context :: sendToClient(id clientID, char* message)
{
#if defined(_DEBUG) && defined(DV_SERVER_VERBOSE) && defined(DV_SERVER_VERBOSE_SENDED_DATA)
    std :: stringstream ss;
    ss << "Client [" << clientID.first << "] data sended: " << std :: endl << message;
    log(ss.str());
#endif
    if (!srv.sendToClient(clientID, message))
        delete message;
}

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

void global_context :: startEngine(void)
{
    eng.start();
}

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

void global_context :: stopEngine(void)
{
    eng.stop();
}

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

#include "fortress_skelet.h"
startingGameResult global_context :: startGame(const std :: string& server, const std :: string& database, 
		const std :: string& username, const std :: string& password, int port, int maxConnections)
{
    // nastartuj DB vsech kontextu - nyni pouze jednoho
    LOG("Connecting to database....");
    if (!mainContext.db.connect(server, database, username, password)) 
		return SGR_FAIL;

    LOG("Initializing request table....");
    init_request_table();

    // nacteme datum a cas
    LOG("Initializing time....");
    gameTimeEQWallTime.second    = dv_time :: now();
    gameTimeEQWallTime.first     = actx->db.getDoubleSettings(STT_GAME_TIME);
            
    // inicializuj cache listy
    actx->initialize_cache_lists();
	
	// nastavime synchronizaci na nyni
    actx->lastSynchronization = dv_time :: now();

    // vytvor index hvezd
    LOG("Building star index....");
    actx->spaceObjectIndex.build(actx);

	// nastav startingPlanet
	LOG("Finding Terra....");
	unsigned long planetsFound = 0;
	for (unsigned long loop1 = 0; loop1 < actx->cacheSpaceObjectList.getSize(); loop1++)
	{
		if (actx->cacheSpaceObjectList.getItem<spacePlanetObject>(loop1))
		{
			planetsFound++;
			if (STARTING_PLANET_ORDER == planetsFound)
			{
				actx->startingPlanet = actx->cacheSpaceObjectList.getItem<spacePlanetObject>(loop1);
			}
		}
		if (actx->startingPlanet)
		{
			break;
		}
	}

    // nastartuj engine
    LOG("Starting engine....");
    eng.start();

	bool creatingSpace = 0;
	if (!(actx->startingPlanet))
	{
		LOG("Creating small space....");
		spaceGenerator::createNewSmallSpace();
		creatingSpace = 1;
	}

    LOG("Initializing events");
    actx->initialize_events();

	scientificAndMedicalPhaseGlobalTriggerClass::registerNextTriggerInstance();

    // nastartuj TCP/IP server
    LOG("Starting TCP/IP server....");
    if (!srv.start(port, maxConnections))
    {
        LOG("Error, stopping engine....");
        eng.stop();
        LOG("Disconnecting database....");
        mainContext.db.disconnect();

        return SGR_FAIL;
    }

    if (creatingSpace)
	{
		return SGR_SPACE;
	}
	else
	{
		LOG("OK, game running....");
		return SGR_OK;
	}
}

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

bool global_context :: createUniverse(const std :: string& server, const std :: string& database, 
	const std :: string& username, const std :: string& password, double stars)
{
    // nastartuj DB vsech kontextu - nyni pouze jednoho
    LOG("Connecting to database....");
    if (!mainContext.db.connect(server, database, username, password)) 
        return false;
            
    LOG("Initializing request table....");
    init_request_table();
            
    // inicializuj cache listy
    actx->initialize_cache_lists();
	
	// nastavime synchronizaci na nyni
    actx->lastSynchronization = dv_time :: now();

    // vytvor index hvezd
    LOG("Building star index....");
    actx->spaceObjectIndex.build(actx);

    LOG("Creating circle space...");
    spaceGenerator :: createSpace(stars);

    // nacteme datum a cas
    LOG("Initializing time....");
    gameTimeEQWallTime.first     = actx->db.getDoubleSettings(STT_GAME_TIME);
    gameTimeEQWallTime.second    = dv_time :: now();

    LOG("Synchronizing database....");
    actx->synchronize();

    LOG("Cleaning space object index...");
    mainContext.spaceObjectIndex.clean();

    LOG("Disconnecting from database...");
    mainContext.db.disconnect();

    return true;
}

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

void context :: initialize_events(void)
{
    cacheRealmsList.updateEvents();
    cacheFortressesList.updateEvents();
    cacheFortressClassesList.updateEvents();
    cacheItemsList.updateEvents();
    cacheItemsPatternsList.updateEvents();
    cacheNullItemsList.updateEvents();
    cacheSpaceObjectList.updateEvents();
}

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

void context :: synchronize(bool force)
{
    if (force || (dv_time :: now() > (lastSynchronization + sync_interval)))
    {
        dv_time :: datetime baseWallTime = gameTimeEQWallTime.second;
        dv_time :: datetime beginSync    = dv_time :: now();
        dv_time :: datetime gtNow        = wt2gt(beginSync);

        cacheRealmsList.synchronize();
        cacheFortressesList.synchronize();
        cacheFortressClassesList.synchronize();
        cacheItemsList.synchronize();
        cacheItemsPatternsList.synchronize();
        cacheNullItemsList.synchronize();
        cacheSpaceObjectList.synchronize();

        dv_time :: datetime endSync      = dv_time :: now();
        gameTimeEQWallTime.second        = baseWallTime + endSync - beginSync;

        // ulozime datum a cas
        db.setDoubleSettings(STT_NORMAL_TIME, endSync);
        db.setDoubleSettings(STT_GAME_TIME, gtNow);

        lastSynchronization = endSync;

        actx->db.commit();
    }
}

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

void context :: initialize_cache_lists(void)
{
    LOG("Initializing realms cache list...");
    cacheRealmsList.DB_Initialize();

    LOG("Initializing fortress cache list...");
    cacheFortressesList.DB_Initialize();

    LOG("Initializing fortress classes cache list...");
    cacheFortressClassesList.DB_Initialize();

    LOG("Initializing items cache list...");
    cacheItemsList.DB_Initialize();

    LOG("Initializing item patterns cache list...");
    cacheItemsPatternsList.DB_Initialize();

    LOG("Initializing null items cache list...");
    cacheNullItemsList.DB_Initialize();

    LOG("Initializing space objects cache list...");
    cacheSpaceObjectList.DB_Initialize();   
}

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

void global_context :: stopGame()
{

    LOG("Stopping TCP/IP server...");
    srv.stop();

    LOG("Stopping engine...");
    eng.stop();

    LOG("Cleaning space object index...");
    mainContext.spaceObjectIndex.clean();

    LOG("Disconnecting from database...");
    mainContext.db.disconnect();
}

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

void updateFortress(dtIdentifier fortressID, dtIdentifier realmID) 
{
    protocolWriter w;
    w.beginMessage(MT_UPDATE, UPDATE_FORTRESS);
    w.writeValue(UPDATE_FORTRESS_FORTRESS_ID, fortressID);
    w.endMessage(true);

    gctx.sendToClient(std::make_pair(realmID, 0), w.copyMessage());
}

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

void startReadOnlyRequest()
{
	gctx.eng.suppressClientUpdates();
}

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

void stopReadOnlyRequest()
{
	gctx.eng.renewClientUpdates();
}

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