
#include "common.h"

#include <list>

#include "spaceindex.h"
#include "delaunay_triangulation.h"
#include "context.h"
#include "fortress_skelet.h"

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

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

void spaceIndex :: clean()
{
    index_.clear();
    spaces_.clear();

    for (std :: vector<spaceIndexItem*> :: iterator iter = items_.begin(); iter != items_.end(); iter++)
    {
        delete (*iter);
    }
    items_.clear();
}

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

spaceIndexItem* spaceIndex :: getItem(dtIdentifier id)                      
{ 
    return spaces_[id];               
}

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

spaceIndexItem* spaceIndex :: getNearestItem(const vector_dist_t& position) 
{ 
    return index_.nearest(convert_vec<double, dist_t>(position));  
}

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

void spaceIndex :: addStar(spaceIndexItem* star)
{ 
    spaces_[star->spaceSystemIndex_] = star;
    items_.push_back(star);           
}

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

spaceIndex :: ~spaceIndex()                                                 
{ 
    clean();                          
}

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

void spaceIndex :: moveShip(dtIdentifier ship, dtIdentifier from, dtIdentifier to)
{
    addShip(ship, to);
    removeShip(ship, from);
}

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

void spaceIndex :: addShip(dtIdentifier ship, dtIdentifier to)
{
    for (std :: multimap<dtIdentifier, dtIdentifier> :: const_iterator iter = ships.lower_bound(to);
        iter != ships.upper_bound(to); iter++)
    {
        if (iter->second == ship)
            return;
    }
    ships.insert(std :: make_pair(to, ship));
}

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

void spaceIndex :: removeShip(dtIdentifier ship, dtIdentifier from)
{
    for (std :: multimap<dtIdentifier, dtIdentifier> :: iterator iter = ships.lower_bound(from);
        iter != ships.upper_bound(from); iter++)
    {
        if (iter->second == ship)
        {
            ships.erase(iter);
            return;
        }
    }
}

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

void spaceIndex :: getShipsInArea(dtIdentifier area, std :: list<dtIdentifier>& shipsRS)
{
    for (std :: multimap<dtIdentifier, dtIdentifier> :: iterator iter = ships.lower_bound(area);
        iter != ships.upper_bound(area); iter++)
    {
        shipsRS.push_back(iter->second);
    }
}

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

void spaceIndex :: build(context* myctx)
{
    /* insertujeme do k2 stromu */
    std :: vector<spaceIndexItem*> titems = items_;
    index_.buildTree(titems);

    /* nacteme aktualni pozice lodi z databaze */
    std :: deque<dtIdentifier> fortresses;
    myctx->cacheFortressesList.getObjectIds(fortresses);

    /* zaneseme lodi do indexu, pripadne upravime aktualni hvezdy */
    for (std :: deque<dtIdentifier> :: const_iterator iter = fortresses.begin(); iter != fortresses.end(); iter++)
    {
        shipClass* shp = myctx->cacheFortressesList.getItem<shipClass>(*iter);
        if (shp == NULL)
            continue;
        else
        {
            vector_dist_t position = shp->MovementGetShipPosition();
            spaceIndexItem* itm = getNearestItem(position);
            if (itm != NULL)
            {
                shp->MovementSetNearestStar(itm->spaceSystemIndex_);
                addShip(shp->getID(), itm->spaceSystemIndex_);
                shp->MovementUpdateShipMovementStatus();
            } 
            else
            {
                errExceptionError("Cannot find nearest star for position (%u, %u) ]", (unsigned long long)position.x,
                    (unsigned long long)position.y);
            }
        }
    }
}

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

spaceIndexItem :: spaceIndexItem(const dtIdentifier ssi, const vector_dist_t& pos)
{
    spaceSystemIndex_ = ssi;
    position_ = convert_vec<double, dist_t>(pos);
}

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

void spaceIndex :: starsByRegion(const dist_t xmin, const dist_t xmax, 
        const dist_t ymin, const dist_t ymax, std :: list<dtIdentifier>& result)
{
    std :: list<spaceIndexItem*> sti;
    index_.region(xmin, xmax, ymin, ymax, sti);

    for (std :: list<spaceIndexItem*> :: const_iterator iter = sti.begin(); iter != sti.end(); iter++)
    {
        result.push_back((*iter)->spaceSystemIndex_);
    }
}

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