// ################################
// ##  Obecna sablona pro listy  ##
// ##      hernich objektu       ##
// ################################

#include "cache_list.h"
#include "error_handling.h"
#include "players_realms.h"

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

// ################################
// ################################

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

void cacheListClass :: DB_Insert(dataListObjectClass* newItem)
{
	newItem->DB_TInsert();
}

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

bool cacheListClass :: isPositionValid(dtIdentifier position) const
{
	return ((NOT_VALID_ID < position) && (position < data.size()));
};

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

void cacheListClass :: expand(dtIdentifier id, bool fillFreeId)
{
    if (id >= data.size())
    {
        dtIdentifier oldSize = (dtIdentifier)data.size();
        data.resize(id);

        // pokud mame, vyplnime volna idcka
        if (fillFreeId)
        {
            for (dtIdentifier i = oldSize; i < id; i++)
            {
                data[i] = std :: make_pair(DATA_IS_EMPTY, (dataListObjectClass*)NULL);
                if (i > 0)
                    freeIDs.insert(i);
            }
        }
    }
};

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

dtIdentifier cacheListClass :: alloc( void )
{
    if (freeIDs.size() > 0)
    {
        dtIdentifierSet :: iterator fidIter = freeIDs.begin();
        dtIdentifier fid = *fidIter;
        freeIDs.erase(fidIter);
        return fid;
    }
    else
    {
        expand(std :: max<dtIdentifier>(2, (dtIdentifier)data.size() * 2), true);
        return alloc();
    }
}

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

dataListObjectClass * cacheListClass :: access(dtIdentifier id)
{
    if (!( isPositionValid(id)))
    {
        // nastala chyba, chceme nacist neplatne id
        //errLogicalError("Reading invalid position %u from cache list!", (unsigned long long) id);
        return NULL;
    }
    else
    {
        switch (data[id].first)
        {
        case DATA_IS_EMPTY:            
            // errLogicalError("Reading empty object from position %u from cache list!", (unsigned long long) id);
            break;
        case DATA_IN_DATABASE_ONLY:
            // nacteme to z databaze
            DB_Load(id);

            if (data[id].first == DATA_SYNCHRONIZED)
            {
                data[id].first = DATA_DESYNCHRONIZED;
                return data[id].second;
            }
            else
            {
                return NULL;
            }
            break;
        case DATA_IN_MEMORY_ONLY:
            return data[id].second;
            break;
        case DATA_IN_DATABASE_INVALID:
            // errLogicalError("Reading empty object from position %u from cache list!", (unsigned long long) id);
            break;
        case DATA_SYNCHRONIZED:
            data[id].first = DATA_DESYNCHRONIZED;
            return data[id].second;
            break;
        case DATA_DESYNCHRONIZED:
            return data[id].second;
            break;
        default:
            errLogicalError("Invalid state at position %u in the cache list!", (unsigned long long) id);
        }
    }

    return NULL;
}

// ================================

cacheListClass :: ~cacheListClass()
{
	for (typedDataListObjectClassPPairVector :: iterator litp1 = data.begin(); litp1 != data.end(); litp1++)
	{
		if (NULL != (litp1->second))
		{
			delete litp1->second;
		}
	}
    freeIDs.clear();
    data.clear();
};

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

bool cacheListClass :: containsData(dtIdentifier id) const
{
    if (!isPositionValid(id))
        return false;

    const dataListObjectType tp = data[id].first;
    
    return (tp == DATA_IN_DATABASE_ONLY) ||
        (tp == DATA_IN_MEMORY_ONLY) ||
        (tp == DATA_SYNCHRONIZED) ||
        (tp == DATA_DESYNCHRONIZED);
}

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

dataListObjectClass * cacheListClass::operator [] (unsigned long position)
{
	return access(position);
};

// ================================

unsigned long cacheListClass::insertItem(dataListObjectClass * newItem)
{
	dtIdentifier newID = alloc();

	if (newID >= (dtIdentifier)data.size())
	{
		errLogicalError("%u greater than data.size() = %u",(unsigned long long)newID,(unsigned long long)data.size());
		return NOT_VALID_ID;
	}

	if (NULL != data[newID].second)
	{
		errLogicalError("data[%u].second not null",(unsigned long long)newID);
		return NOT_VALID_ID;
	}

    switch (data[newID].first)
    {
    case DATA_IS_EMPTY:
        data[newID] = make_pair(DATA_IN_MEMORY_ONLY, newItem);        
        break;
    case DATA_IN_DATABASE_ONLY:
        errLogicalError("Cannot insert at position %u in the cache list, item already in database!", (unsigned long long) newID);
        freeIDs.insert(newID);
        newID = 0;
        break;
    case DATA_IN_MEMORY_ONLY:
        errLogicalError("Cannot insert at position %u in the cache list, item already in main memory!", (unsigned long long) newID);
        freeIDs.insert(newID);
        newID = 0;
        break;
    case DATA_IN_DATABASE_INVALID:
        data[newID] = make_pair(DATA_DESYNCHRONIZED, newItem);

        break;
    case DATA_SYNCHRONIZED:
        errLogicalError("Cannot insert at position %u in the cache list, item already in cache list!", (unsigned long long) newID);
        freeIDs.insert(newID);
        newID = 0;
        break;
    case DATA_DESYNCHRONIZED:
        errLogicalError("Cannot insert at position %u in the cache list, item already in cache list!", (unsigned long long) newID);
        freeIDs.insert(newID);
        newID = 0;
        break;
    default:
        errLogicalError("Invalid state at position %u in the cache list!", (unsigned long long) newID);
        freeIDs.insert(newID);
        newID = 0;
    }

    newItem->setID(newID);

	return newID;
};

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

void cacheListClass :: deleteItem(dtIdentifier position)
{
	if (!(isPositionValid(position)))
		return;

    switch (data[position].first)
    {
    case DATA_IS_EMPTY:
        errLogicalError("Deleting empty object from position %u from cache list!", (unsigned long long) position);
        break;
    case DATA_IN_DATABASE_ONLY:
        data[position] = make_pair(DATA_IN_DATABASE_INVALID, (dataListObjectClass *) NULL);
        break;
    case DATA_IN_MEMORY_ONLY:
        delete data[position].second;
        data[position] = make_pair(DATA_IS_EMPTY, (dataListObjectClass *) NULL);
        break;
    case DATA_IN_DATABASE_INVALID:
        errLogicalError("Deleting empty object from position %u from cache list!", (unsigned long long) position);
        break;
    case DATA_SYNCHRONIZED:
        delete data[position].second;
        data[position] = make_pair(DATA_IN_DATABASE_INVALID, (dataListObjectClass *) NULL);
        break;
    case DATA_DESYNCHRONIZED:
        delete data[position].second;
        data[position] = make_pair(DATA_IN_DATABASE_INVALID, (dataListObjectClass *) NULL);
        break;
    default:
        errLogicalError("Invalid state at position %u in the cache list!", (unsigned long long) position);
    }
    
    if (identifiersReusable())
        freeIDs.insert(position);
};

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

void cacheListClass :: synchronize(void)
{
    // synchronizujeme cache list s databazi
    for (dtIdentifier iter = 0; iter < data.size(); iter++)
    {
        switch (data[iter].first)
        {
        case DATA_IN_MEMORY_ONLY:
            data[iter].second->DB_TInsert();
            data[iter].first = DATA_SYNCHRONIZED;
            break;
        case DATA_IN_DATABASE_INVALID:
            DB_Remove(iter);
            data[iter].first = DATA_IS_EMPTY;
            break;
        case DATA_DESYNCHRONIZED:
            data[iter].second->DB_TSave();
            data[iter].first = DATA_SYNCHRONIZED;
            break;
        }
    }
}

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

void cacheListClass :: getObjectIds(std :: deque<dtIdentifier>& ids )
{
    for (unsigned int i = 0; i < data.size(); i++)
    {
        if (containsData(i))
            ids.push_back(i);
    }
}

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

void  cacheListEventInitializableClass :: updateEvents(void)
{
    std :: deque<dtIdentifier> ids;
    getObjectIds(ids);

    for (std :: deque<dtIdentifier> :: const_iterator iter = ids.begin(); iter != ids.end(); iter++)
    {
        (operator[] (*iter))->UpdateEvents();
    }
}

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

dtIdentifier cacheRealmsListClass :: insertItem(dataListObjectClass * newItem)
{
    realmClass* rc = dynamic_cast<realmClass*>(newItem);

    if (rc == NULL)
    {
        errExceptionError("Bad typecast!");
        return NOT_VALID_ID;
    }

    std :: string login = rc->getLogin();
    std :: string name  = rc->getPlayerName();
    std :: map<std :: string, dtIdentifier> :: const_iterator iter1 = logins.find(login);
    std :: map<std :: string, dtIdentifier> :: const_iterator iter2 = names.find(name);

    if ((iter1 == logins.end()) && (iter2 == names.end()))
    {
        dtIdentifier id = cacheListClass :: insertItem(newItem);
        if (id != NOT_VALID_ID)
        {
            logins[login] = id;
            names[name] = id;
        }

        return id;
    }
    else
    {
        return NOT_VALID_ID;
    }
}

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

void cacheRealmsListClass :: deleteItem(dtIdentifier id)
{
    realmClass* rc = dynamic_cast<realmClass*>(access(id));
    if (rc != NULL)
    {
        logins.erase(rc->getLogin());
        names.erase(rc->getPlayerName());
    }

    cacheListClass :: deleteItem(id);
}

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

unsigned long cacheRealmsListClass :: idByLogin(  const std :: string& login)
{
    std :: map<std :: string, dtIdentifier> :: const_iterator iter = logins.find(login);
    if (iter != logins.end())
    {
        return iter->second;
    }
    else
    {
        return 0;
    }
}

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

unsigned long cacheRealmsListClass :: idByName(  const std :: string& name)
{
    std :: map<std :: string, dtIdentifier> :: const_iterator iter = names.find(name);
    if (iter != names.end())
    {
        return iter->second;
    }
    else
    {
        return 0;
    }
}

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

bool cacheRealmsListClass :: loginExists(  const std :: string& login)
{
    return logins.find(login) != logins.end();
}

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

bool cacheRealmsListClass :: nameExists(  const std :: string& name)
{
    return names.find(name) != names.end();
}

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

dataListObjectClass *   cacheSpaceObjectListClass :: access     (dtIdentifier          id        )
{
    if (!( isPositionValid(id)))
    {
        // nastala chyba, chceme nacist neplatne id
        //errLogicalError("Reading invalid position %u from cache list!", (unsigned long long) id);
        return NULL;
    }
    else
    {
        switch (data[id].first)
        {
        case DATA_IS_EMPTY:
            //errLogicalError("Reading empty object from position %u from cache list!", (unsigned long long) id);
            break;
        case DATA_IN_DATABASE_ONLY:
            // nacteme to z databaze
            DB_Load(id);

            if (data[id].first == DATA_SYNCHRONIZED)
            {
                data[id].first = DATA_DESYNCHRONIZED;
                return data[id].second;
            }
            else
            {
                return NULL;
            }
            break;
        case DATA_IN_MEMORY_ONLY:
            return data[id].second;
            break;
        case DATA_IN_DATABASE_INVALID:
            //errLogicalError("Reading empty object from position %u from cache list!", (unsigned long long) id);
            break;
        case DATA_SYNCHRONIZED:

            // rozlisime, zda se jedna o zakladnu, ukladame jen zakladny
            if (dynamic_cast<spaceObject*>(data[id].second)->getType() == SOT_BASE)
            {
                data[id].first = DATA_DESYNCHRONIZED;
            }

            return data[id].second;
            break;
        case DATA_DESYNCHRONIZED:
            return data[id].second;
            break;
        default:
            errLogicalError("Invalid state at position %u in the cache list!", (unsigned long long) id);
        }
    }

    return NULL;
}

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

// ################################
// ################################