
#include "common.h"

#include <cmath>
#include <vector>
#include <sstream>
#include <iostream>

#include "context.h"
#include "space.h"
#include "datetime.h"
#include "fortress_skelet.h"
#include "mathfunc.h"
#include "log.h"

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

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

dtIdentifier spaceObjectLeastCommonParent(dtIdentifier v1, dtIdentifier v2)
{
	return spaceObjectLeastCommonParent(actx->cacheSpaceObjectList.getItem<spaceObject>(v1), actx->cacheSpaceObjectList.getItem<spaceObject>(v2));
}

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

dtIdentifier spaceObjectLeastCommonParent(spaceObject* v1, spaceObject* v2)
{
    std :: vector<dtIdentifier> v1Path;
    std :: vector<dtIdentifier> v2Path;

    v1Path.push_back(v1->getID());
    v1Path.push_back(v2->getID());

    dtIdentifier cid;

    cid = v1->getID();
    while (cid != 0)
    {
		cid = actx->cacheSpaceObjectList.getItem<spaceObject>(cid)->getParentSpaceObject();
        v1Path.push_back(cid);
    }

    cid = v2->getID();
    while (cid != 0)
    {
        cid = actx->cacheSpaceObjectList.getItem<spaceObject>(cid)->getParentSpaceObject();
        v2Path.push_back(cid);
    }

    std :: vector<dtIdentifier> :: reverse_iterator i1 = v1Path.rbegin();
    std :: vector<dtIdentifier> :: reverse_iterator i2 = v2Path.rbegin();

    dtIdentifier retval = 0;
    while (i1 != v1Path.rend() && i2 != v2Path.rend())
    {
        retval = *i1 == *i2 ? *i1 : retval;
        i1++, i2++;
    }

    return retval;
}

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

dtIdentifier spaceObjectGetStarSystem(dtIdentifier spaceObj)
{
	return actx->cacheSpaceObjectList.getItem<spaceObject>(spaceObj)->getStarSystem();
}

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

void spaceObject :: getWholeTreeIds(std :: set<dtIdentifier>& ids) const
{
    ids.insert(getID());
}

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

vector_dist_t spaceFortressObject :: getAbsolutePosition(void) const
{
    return actx->cacheSpaceObjectList.getItem<spaceObject>(getParentSpaceObject())->getAbsolutePosition();
}

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

dtIdentifier spaceObject :: getStarSystem(void) 
{
    dtIdentifier currentID    = getParentSpaceObject();
    dtIdentifier oldCurrentID = getID();
    while (currentID != 0)
    {
		spaceRegularObject* so = actx->cacheSpaceObjectList.getItem<spaceRegularObject>(currentID);
        oldCurrentID    = currentID;
        currentID       = so->getParentSpaceObject();
    }

    return oldCurrentID;
}

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

bool spaceRegularObject :: getNearestCrossingPoint(const vector_dist_t& course, const vector_dist_t& basicpt, 
                                            vector_dist_t& rslt)
{
    const dist_t a = -course.y;
    const dist_t b = course.x;
    const dist_t c = vector_dist_t(-course.y, course.x) * basicpt;
    const dist_t f = position_.x;
    const dist_t g = position_.y;
    const dist_t r = areaRadius_;
    
    std :: list<vector_dist_t> pts;

    /* pokud a neni nulove, spocitej to pres kvadraticky vzorecek */
    if (a != 0)
    {
        const dist_t sqFactor = (b*b+a*a)*r*r-b*b*g*g+2*g*(b*c-a*b*f)-a*a*f*f+2*a*c*f-c*c;
        if (sqFactor < 0) return false;

        const dist_t sqrtOfsqFactor = (dist_t)sqrt((double)sqFactor);
        const dist_t y1 = - (a*sqrtOfsqFactor-a*a*g+a*b*f-b*c)/(a*a+b*b);
        const dist_t y2 = + (a*sqrtOfsqFactor+a*a*g-a*b*f+b*c)/(a*a+b*b);
        const dist_t x1 = (c-b*y1) / a;
        const dist_t x2 = (c-b*y2) / a;

        pts.push_back(vector_dist_t(x1, y1));
        pts.push_back(vector_dist_t(x2, y2));
    }
    else
    {
        const dist_t y = c / b;
        const dist_t sqFactor = b*b*(r*r-g*g)+2*b*c*g-c*c;
        if (sqFactor < 0) return false;

        const dist_t sqrtOfsqFactor = (dist_t)sqrt((double)sqFactor);
        const dist_t x1 = -(sqrtOfsqFactor - b*f) / b;
        const dist_t x2 =  (sqrtOfsqFactor + b*f) / b;

        pts.push_back(vector_dist_t(x1, y));
        pts.push_back(vector_dist_t(x2, y));
    }

    /* pretridime ty, ktere lezi ve smeru kurzu */
    std :: list<vector_dist_t> candidates;
    for (std :: list<vector_dist_t> :: iterator iter = pts.begin(); iter != pts.end(); iter++)
    {
        if (((iter->x - basicpt.x)*course.x > 0) || ((iter->y - basicpt.y)*course.y > 0))
        {
            /* kladny cas k dosazeni bodu */
            candidates.push_back(*iter);
        }
    }

    /* vybereme nejblizsiho kandidata */
    if (candidates.size() == 0) return false;
    rslt = *candidates.begin();
    dist_t minDist = (rslt - basicpt).squaredNorm();
    for (std :: list<vector_dist_t> :: iterator iter = candidates.begin(); iter != candidates.end(); iter++)
    {
        dist_t curDist = (*iter - basicpt).squaredNorm();
        if (curDist < minDist)
        {
            rslt = *iter;
            minDist = curDist;
        }
    }

    return true;
}

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

vector_dist_t  spaceRegularObject :: getPosition(void) const
{ 
    if (parentObject_ == 0)
    {
        return position_;                                                   
    } 
    else 
    {        
        return updatePositionOrbiting(vector_dist_t(0,0), distanceFromParentObject_, getAngle());
    }
}

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

vector_dist_t spaceRegularObject :: getAbsolutePosition(void) const
{
    if (parentObject_ == 0)
    {
        return position_;
    }
    else
    {
		return getPosition() + actx->cacheSpaceObjectList.getItem<spaceRegularObject>(parentObject_)->getAbsolutePosition();
    }
}

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

dist_t spaceRegularObject :: getAngle(void) const
{
    dv_time:: datetime dt = wt2gt(dv_time:: now());
    return (double)((unsigned long long)(((convertDatetimeToSeconds(dt) * (double)angularVelocity_) + 
        unitsConversion<double> :: degreesToNanoDegrees((double)basicRotationAngle_))) 
        % (unsigned long long)unitsConversion<double> :: degreesToNanoDegrees(360));
}

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

void spaceRegularObject :: getWholeTreeIds(std :: set<dtIdentifier>& ids) const
{
    ids.insert(getID());

    for (dtIdentifierConstIterator iter = subObjectsBegin(); iter != subObjectsEnd(); iter++)
    {
        spaceObject* sobj = actx->cacheSpaceObjectList.getItem<spaceObject>(*iter);
        if (sobj != NULL)
        {
            std :: set<dtIdentifier> subobjects;
            sobj->getWholeTreeIds(subobjects);

            ids.insert(subobjects.begin(), subobjects.end());
        }
        else
        {
            errLogicalError("Space area with id %u not found!", (unsigned long long)(*iter));
        }
    }
}

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

const char* star_names[] = { "Andromeda", "Antlia", "Apus", "Aquila", "Aquarius", "Ara",
    "Aries", "Auriga", "Bootes", "Caelum", "Camelopardalis", "Capricornus", "Carina",
    "Cassiopeia", "Centaurus", "Cepheus", "Cetus", "Circinus", "Canis Major", "Canis Minor",
    "Cancer", "Columba", "Coma Berenices", "Corona Austrina", "Corona Borealis", "Canes Venatici", "Crater",
    "Crux", "Corvus", "Cygnus", "Delphinus", "Dorado", "Draco", "Equuleus", "Eridanus", "Fornax",
    "Gemini", "Grus", "Hercules", "Horologium", "Hydrus", "Chameleon", "Indi", "Lacerta", "Leo", "Indus",
    "Lepus", "Libra", "Leo Minor", "Lupus", "Lynx", "Lyra", "Mensa", "Monoceros", "Musca", "Octans",
    "Ophiuchus", "Orion", "Pavo", "Pegasus", "Perseus", "Phoenix", "Pictor", "Piscis Australis", "Pisces",
    "Puppis", "Pyxis", "Reticulum", "Sculptor", "Scorpius", "Scutum", "Serpens", "Sextans", "Sagitta",
    "Sagittarius", "Taurus", "Triangulum", "Tucana", "Ursa Major", "Ursa Minor", "Vela", "Virgo", "Volans",
    "Vulpecula" };

const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

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

spaceObject* spaceObject :: getParentSpaceObjectInstance(void)
{
    return actx->cacheSpaceObjectList.getItem<spaceObject>(getParentSpaceObject());
}

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

static const std :: string generateStarName(unsigned long id)
{
    // inicializuj nahodny generator
    unsigned long long seed = id;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;

    std :: stringstream ss;
    ss << star_names[seed % (sizeof(star_names) / sizeof(char*))] << " ";
    seed = 6364136223846793005 * seed + 1442695040888963407;
    ss << alphabet[seed % (sizeof(alphabet) / sizeof(char))];

    return ss.str();
}

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

static const std :: string generatePlanetName(unsigned long id)
{
    unsigned long long seed = id;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;

    std :: stringstream ss;
    ss << "PX ";
    ss << seed % 10; seed = seed / 10;
    ss << seed % 10; seed = seed / 10;
    ss << seed % 10; seed = seed / 10;
    ss << seed % 10; seed = seed / 10;
    ss << seed % 10; seed = seed / 10;

    return ss.str();
}

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

long spacePlanetObject :: getTexture(void) const
{
	if (this->getID() == actx->startingPlanet->getID())
	{
		return PLANET_TEXTURE_GRASS;
	}

	if (DEEP_ICE_TEMPERATURE >= this->getDayTemperature())
	{
		return PLANET_TEXTURE_FROZEN;
	}
	if (VOLCANO_DAY_TEMPERATURE   <= this->getDayTemperature())
	{
		if (VOLCANO_NIGHT_TEMPERATURE <= this->getNightTemperature())
		{
			return PLANET_TEXTURE_VOLCANO;
		}
		else
		{
			return PLANET_TEXTURE_DESERT;
		}
	}
	if (GAS_TITAN == this->getPlanetType())
	{
		if ((0 < this->getSea()                                                                    ) ||
			(ILM_GAS_MAX_LIMIT - ILM_GAS_MAX_LIMIT * ALIEN_EDGE_OF_ILMENITE < this->ilmeniteSource_))
		{
			return PLANET_TEXTURE_ALIEN;
		}
		else
		{
			return PLANET_TEXTURE_CRYSTAL;
		}
	}
	else
	{
		if ((THE_BEST_SOURCE - THE_BEST_SOURCE * GRASS_EDGE_OF_HEWZRC < this->zirconiumSource_ ) ||
			(THE_BEST_SOURCE - THE_BEST_SOURCE * GRASS_EDGE_OF_HEWZRC < this->heavyWaterSource_))
		{
			return PLANET_TEXTURE_GRASS;
		}
		if (THE_BEST_SOURCE - THE_BEST_SOURCE * MOUNTAIN_EDGE_OF_ILM < this->ilmeniteSource_)
		{
			return PLANET_TEXTURE_MOUNTAINS;
		}
	}

    unsigned long long seed = getID();
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;
    seed = 6364136223846793005 * seed + 1442695040888963407;

    return (long)(seed % PLANET_MAX_NUMBER_OF_TERRAINS);
}

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

const std :: string spacePlanetObject :: getName(void) const
{
	if (this->getID() != actx->startingPlanet->getID())
	{
		return generatePlanetName(getID());
	}
	else
	{
		return string("Terra");
	}
}

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

const std :: string spaceSunObject :: getName(void) const
{
    return generateStarName(getID());
}

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

dtIdentifier spaceFortressObject :: createWithFortress( const dtIdentifier parentSpaceObject, baseClass* base)
{
    spaceFortressObject* f = new spaceFortressObject(base->getID());
    dtIdentifier sfoId     = actx->cacheSpaceObjectList.insertItem(f);

    if (sfoId != 0)
    {
        f->parentObject_        = parentSpaceObject;
        spacePlanetObject* spo  = actx->cacheSpaceObjectList.getItem<spacePlanetObject>(parentSpaceObject);
        if (spo != NULL)
        {
            spo->subObjectAdd(sfoId);
        }
        else
        {
            // pri nenalezeni planety smazeme objekt a nevratime nic
            actx->cacheSpaceObjectList.deleteItem(sfoId);
            sfoId = 0;
        }
    }

    base->setSpaceObject(sfoId);

    return sfoId;
}

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

void getShipsAndBasesInArea(dtIdentifier area, std :: list<dtIdentifier>& ships, 
                            std :: list<dtIdentifier>& bases, bool searchSubareas)
{
    // nejdrive nalezneme oblasti, ktere je nutne prohledat
    std :: set<dtIdentifier> areaIds;

    spaceObject* sobj = actx->cacheSpaceObjectList.getItem<spaceObject>(area);
    if (sobj != NULL)
    {
        // vytahneme oblasti, ktere nas zajimaji
        if (searchSubareas)
            sobj->getWholeTreeIds(areaIds);
        else
            areaIds.insert(area);

        // vyfiltrujeme zakladny
        for (std :: set<dtIdentifier> :: const_iterator iter = areaIds.begin(); iter != areaIds.end(); iter++)
        {
            spaceFortressObject* fobj = actx->cacheSpaceObjectList.getItem<spaceFortressObject>(*iter);
            if (fobj != NULL)
                bases.push_back(fobj->getFortressID());
        }

        // vyfiltrujeme lode
        dtIdentifier starId = spaceObjectGetStarSystem(area);

        std :: list<dtIdentifier> unfShips;
        actx->spaceObjectIndex.getShipsInArea(starId, unfShips);

        for (std :: list<dtIdentifier> :: iterator iter = unfShips.begin(); iter != unfShips.end(); iter++)
        {
            shipClass* shp = actx->cacheFortressesList.getItem<shipClass>(*iter);
            if (shp != NULL)
            {
                if (areaIds.find(shp->MovementGetSpaceObjectActuallyIn()) != areaIds.end())
                {
                    // tato lod je v zajmu objektu
                    ships.push_back(*iter);
                }
            }
            else
            {
                errLogicalError("Ship with id %u not found!", (unsigned long long)(*iter));
            }
        }
    }
    else
    {
        errLogicalError("Space area with id %u not found!", (unsigned long long)(area));
    }
}

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

squareGenerator :: squareGenerator(const dist_t xmin, const dist_t xmax, const dist_t ymin, const dist_t ymax)
{
    xmin_ = xmin;
    xmax_ = xmax;
    ymin_ = ymin;
    ymax_ = ymax;
}

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

vector_dist_t squareGenerator :: generatePoint(void) const
{
    return vector_dist_t(xmin_ + (xmax_-xmin_)*rand01(), ymin_ + (ymax_-ymin_)*rand01());
}

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

circleGenerator :: circleGenerator(const vector_dist_t base, const dist_t radius)
{
    base_ = base;
    radius_ = radius;
}

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

vector_dist_t circleGenerator :: generatePoint(void) const
{
    double phi = rand01() * PI2;
    double rd  = rand01();

    return vector_dist_t(radius_ * cos(phi) * rd + base_.x, radius_ * sin(phi) * rd + base_.y); 
}

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

struct spaceGenItem
{
    vector_dbl_t position_;

    vector_dbl_t& getPosition(void) { return position_; }

    spaceGenItem(const vector_dist_t& pos)
    {
        position_ = pos;
    }
};

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

unsigned long spaceGenerator::lengthToStarCount(dist_t length)
{
	double sideCount  = length / MIN_STARS_DISTANCE_IN_KM;
	double starsCount = 8 * pow((double)sideCount, (double)2);
	return (unsigned long)starsCount;
}

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

void spaceGenerator::makeCircleSpace(dist_t radius)
{
	if (!(0 < radius))
	{
		return;
	}
	unsigned long starsCount = spaceGenerator::lengthToStarCount(2 * radius);
	unsigned long indexLimit = ulRound(pow((double)starsCount, (double)0.5)) + actx->cacheSpaceObjectList.getSize();
	circleGenerator generator(vector_dist_t(0, 0), radius);
	spaceGenerator::addStars(indexLimit, starsCount, generator);
}

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

void spaceGenerator::makeSquareSpace(dist_t length)
{
	if (!(0 < length))
	{
		return;
	}
	unsigned long starsCount = spaceGenerator::lengthToStarCount(length);
	unsigned long indexLimit = ulRound(pow((double)starsCount, (double)0.5)) + actx->cacheSpaceObjectList.getSize();
	squareGenerator generator(-1 * length / 2, length / 2, -1 * length / 2, length / 2);
	spaceGenerator::addStars(indexLimit, starsCount, generator);
}

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

void spaceGenerator::createNewSmallSpace()
{
	spaceGenerator::createSpace(MIN_STARS_IN_SPACE);
}

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

void spaceGenerator::createSpace(double stars)
{
	spaceGenerator::makeCircleSpace(sqrt(2 * stars * pow(MIN_STARS_DISTANCE_IN_KM / (double)2, (double)2)));
}

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

void spaceGenerator :: addStars(unsigned long reindexLimit, unsigned long howMany, starPosGenerator & generator)
{
	lockConsoleForWrite();
	cout << "Creating stars...." << endl;
	unlockConsoleForWrite();
    std :: vector<spaceGenItem*> items;    // vsechny hvezdy
    std :: vector<spaceGenItem*> added;    // pouze pridane hvezdy
    std :: vector<spaceGenItem*> worklist; // aktualni pridane hvezdy nezanesene do indexu
    k2tree<spaceGenItem, dist_t> stars;
	bool isWhiteDwarfInSpace = 0;

    // pridame stare hvezdy
    for (std :: vector<spaceIndexItem *> :: const_iterator iter = actx->spaceObjectIndex.starsBegin(); iter != actx->spaceObjectIndex.starsEnd(); iter++)
    {
        spaceGenItem * newItem = new spaceGenItem((*iter)->getPosition());
        items.push_back(newItem);
    }

    std :: vector<spaceGenItem*> tmp = items;
    stars.buildTree(tmp);

	unsigned long perc     = 0;
	unsigned long addedCnt = 0;
	dist_t shortestRange   = 0;
	dist_t minNorm         = 0;
	dist_t avgNorm         = 0;
	dist_t maxNorm         = 0;
    for (unsigned long i = 0; i < howMany; i++)
    {
        spaceGenItem* newItem = new spaceGenItem(generator.generatePoint());
		dist_t normAct = newItem->getPosition().norm();
		
        // zjistime nejblizsi item
		dist_t mindist = MIN_STARS_DISTANCE_IN_KM * 2;
		spaceGenItem * nearestStar = NULL;
		if (NULL != (nearestStar = stars.nearest(newItem->getPosition())))
		{
			mindist = (nearestStar->getPosition() - newItem->getPosition()).norm();
		}
		else
		{
			if (worklist.empty())
			{
				newItem->position_ = vector_dist_t(0, 0);
			}
		}
        for (std :: vector<spaceGenItem*> :: const_iterator iter = worklist.begin(); iter != worklist.end(); iter++)
        {
            mindist = std :: min<dist_t>(mindist, ((*iter)->getPosition()-newItem->getPosition()).norm());
        }

        // pokud je vzdalenost mensi, nez je dovolene (aby soustavy nebyly moc blizko sebe), tak nic nedelame, jinak pridame
		if (MIN_STARS_DISTANCE_IN_KM < mindist)
        {
			if ((shortestRange > mindist) ||
				(0 == shortestRange     ))
			{
				shortestRange = mindist;
			}
			addedCnt++;
			if ((minNorm > normAct) ||
			(0 == minNorm     ))
			{
				minNorm = normAct;
			}
			avgNorm += normAct;
			if ((maxNorm < normAct) ||
				(0 == maxNorm     ))
			{
				maxNorm = normAct;
			}

            // pridame itemy do seznamu
            items.push_back(newItem);
            added.push_back(newItem);
            worklist.push_back(newItem);

            // jestlize je vhodny cas, provedeme rebuild indexu
            if (i % reindexLimit == 0)
            {
                worklist.clear();
                std :: vector<spaceGenItem*> _tmp = items;
                stars.buildTree(_tmp);
            }
        }
        else
        {
            delete newItem;
        }
		if (perc < (i + 1) * HUNDRED_PERCENT / howMany)
		{
			lockConsoleForWrite();
			perc = (i + 1) * HUNDRED_PERCENT / howMany;
			cout << "\rIndexing stars: " << perc << "%";
			if (HUNDRED_PERCENT == perc)
			{
				cout << endl << "Stars has been indexed." << endl;
			}
			unlockConsoleForWrite();
		}
    }
	lockConsoleForWrite();
	cout << "Required stars : " << howMany << "." << endl;
	cout << "Added stars    : " << addedCnt << "." << endl;
	cout << "Shortest range : " << shortestRange / physicsConstants<dist_t>::ly() << "ly." << endl;
	cout << "Star spacing   : " << MIN_STARS_DISTANCE_IN_KM / physicsConstants<dist_t>::ly() << "ly." << endl;
	cout << "Min norm       : " << minNorm / physicsConstants<dist_t>::ly() << "ly." << endl;
	cout << "Avg norm       : " << avgNorm / max(1, addedCnt) / physicsConstants<dist_t>::ly() << "ly." << endl;
	cout << "Max norm       : " << maxNorm / physicsConstants<dist_t>::ly() << "ly." << endl;
	cout << "Space length   : " << generator.getLength() / physicsConstants<dist_t>::ly() << "ly." << endl;
	cout << "Space surface  : " << generator.getSurface() / pow(physicsConstants<dist_t>::ly(), (double)2) << "sly." << endl;
	cout << "Covered surface: " << addedCnt * PI * pow((double)MIN_STARS_DISTANCE_IN_KM / (double)2, (double)2) / pow(physicsConstants<dist_t>::ly(), (double)2) << "sly." << endl;
	unlockConsoleForWrite();

	perc                = 0;
	unsigned long loop1 = 0;
    for (std :: vector<spaceGenItem*> :: const_iterator iter = added.begin(); iter != added.end(); iter++)
    {
        spaceSunObject* newSun = new spaceSunObject((*iter)->getPosition());
        if (0 != actx->cacheSpaceObjectList.insertItem(newSun))
        {
			spaceGenerator :: initializeSolarSystem(newSun, isWhiteDwarfInSpace);
        }
        else
        {
            delete newSun;
        };
		if (perc < (loop1 + 1) * HUNDRED_PERCENT / addedCnt)
		{
			lockConsoleForWrite();
			perc = (loop1 + 1) * HUNDRED_PERCENT / addedCnt;
			cout << "\rCreating solar systems: " << perc << "%";
			if (HUNDRED_PERCENT == perc)
			{
				cout << endl << "All solar systems has been created." << endl;
			}
			unlockConsoleForWrite();
		}
		loop1++;
    }

    // nakonec smazeme naalokovane zdroje
    for (std :: vector<spaceGenItem*> :: iterator iter = items.begin(); iter != items.end(); iter++)
    {
        delete (*iter);
    }

    items.clear();
    added.clear();
    worklist.clear();
    stars.clear();

    actx->spaceObjectIndex.clean();

    std :: deque<dtIdentifier> starsToIndex;
    actx->cacheSpaceObjectList.getObjectIds(starsToIndex);
    for (std :: deque<dtIdentifier> :: const_iterator iter = starsToIndex.begin(); iter != starsToIndex.end(); iter++)
    {
        spaceObject* so = actx->cacheSpaceObjectList.getItem<spaceObject>(*iter);
        if (so != NULL)
        {
            // nejdrive vytvorime space index objekt, pokud je to hvezda
            if (so->getType() == SOT_STAR)
            {
                vector_dist_t pos = so->getAbsolutePosition();
                spaceIndexItem* sii = new spaceIndexItem((const dtIdentifier)so->getID(), pos);
                actx->spaceObjectIndex.addStar(sii);
            }
        }
        else
        {
            errLogicalError("Space object %u is NULL !", (unsigned long long)(*iter));
        }

    }

    actx->spaceObjectIndex.build(actx);
}

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

void spaceGenerator::initializeSolarSystem(spaceSunObject * star, bool & isWhiteDwarfInSpace)
{
    if (!star)
	{
		return;
	}

	//inicializace samotne hvezdy
	star->setParent(0);
	if  ((actx->startingPlanet           )
		 &&
	 	 ((!isWhiteDwarfInSpace         ) ||
		  (rollADice(WHITE_DWARF_CHANCE))))
	{
		star->setStarToBeWhiteDwarf();
		star->setWeight(dRandomNumberFromScale(WHITE_DWARF_WEIGHT_MIN, WHITE_DWARF_WEIGHT_MAX, 2));
		star->setObjectRadius(dRandomNumberFromScale(WHITE_DWARF_RADIUS_MIN, WHITE_DWARF_RADIUS_MAX, 1));
		isWhiteDwarfInSpace = 1;
	}
	else
	{
		star->setStarToBeRegular();
		if (actx->startingPlanet)
		{
			star->setWeight(dRandomNumberFromScale(STAR_WEIGHT_MIN, STAR_WEIGHT_MAX, (unsigned long)200, (double)0.95));
		}
		else
		{
			star->setWeight(SUN_WEIGHT);
		}
		double starCapacity = star->getWeight() / max(STAR_DENSITY_MIN, dRandomNumberFromScale(STAR_DENSITY_MIN, STAR_DENSITY_MAX, 1));
		star->setObjectRadius(pow((double)(starCapacity / ((double)4/(double)3 * PI)), (double)1 / (double)3) / KILO);
	}
	star->setAreaRadius(star->getWeight() * AREA_RADIUS_TO_CENTER_WEIGHT_IN_KM);
	star->setDistanceFromParent(0);
	star->setBasicRotationAngle(0);
	star->setAngularVelocity(0);

	// tvorba slunecni soustavy
	if ((STANDARD == star->getSunType()                                   ) &&
		((!(actx->startingPlanet)) || (rollADice(HABITABLE_SYSTEM_CHANCE))))
	{
		list<spacePlanetObject *> planets;
		planets.clear();
		unsigned long planetCount   = ulNumberFromScale(PLANETS_IN_HABITABLE_SYSTEM_MIN, PLANETS_IN_HABITABLE_SYSTEM_MAX, STAR_WEIGHT_MAX - STAR_WEIGHT_MIN, star->getWeight());
		planetCount                += rollA0ToNSidedDice(PLANETS_IN_HABITABLE_SYSTEM_MAX - planetCount);		
		if (!(actx->startingPlanet))
		{
			planetCount = PLANETS_IN_STARTING_SYSTEM;
		}
		double weightTotal          = 0;
		if (!(actx->startingPlanet))
		{
			planetCount = max(planetCount, EARTH_LOOP1 + 1);
		}

		for (unsigned long loop1 = 0; loop1 < planetCount; loop1++)
		{
			spacePlanetObject * planet   = new spacePlanetObject;
			dtIdentifier        planetID = actx->cacheSpaceObjectList.insertItem(planet);
			planet->setPlanetToBeAPlanet();
			star->addChild(planetID);
			planet->setParent(star->getID());
			if ((actx->startingPlanet) ||
				(EARTH_LOOP1 < loop1 ))
			{
				if (!(rollADice(SMALL_PLANET_CHANCE)))
				{
					planet->setWeight(dRandomNumberFromScale(PLANET_MED_WEIGHT, PLANET_MAX_WEIGHT, (unsigned long)8, (double)0.75));
				}
				else
				{
					planet->setWeight(dRandomNumberFromScale(PLANET_MED_WEIGHT, PLANET_MIN_WEIGHT, (unsigned long)8, (double)0.75));
				}
			}
			else
			{
				if (MERCURY_LOOP1 == loop1)
				{
					planet->setWeight(MERCURY_WEIGHT);
				}
				if (VENUS_LOOP1 == loop1)
				{
					planet->setWeight(VENUS_WEIGHT);
				}
				if (EARTH_LOOP1 == loop1)
				{
					planet->setWeight(EARTH_WEIGHT);
				}
			}
			weightTotal += planet->getWeight();
			planets.push_back(planet);
		}

		dist_t solarSystemRadius = SOLAR_SYSTEM_MED_RADIUS;
		if (SUN_WEIGHT < star->getWeight())
		{
			solarSystemRadius = dNumberFromScale(SOLAR_SYSTEM_MED_RADIUS, SOLAR_SYSTEM_MAX_RADIUS, STAR_WEIGHT_MAX - SUN_WEIGHT, star->getWeight() - SUN_WEIGHT);
		}
		if (SUN_WEIGHT > star->getWeight())
		{
			solarSystemRadius = dNumberFromScale(SOLAR_SYSTEM_MIN_RADIUS, SOLAR_SYSTEM_MED_RADIUS, SUN_WEIGHT - STAR_WEIGHT_MIN, star->getWeight() - STAR_WEIGHT_MIN);
		}
		dist_t solarDiscWidth    = solarSystemRadius - SOLAR_SYSTEM_INNER_RADIUS;
		dist_t innerCircleEdge   = SOLAR_SYSTEM_INNER_RADIUS;
		dist_t outerCircleEdge   = SOLAR_SYSTEM_INNER_RADIUS;
		bool gassTitansOnly      = 0;
		unsigned long loop1      = 0;
		long dayTemp             = MAX_TEMPERATURE;
		for (list<spacePlanetObject *>::iterator litp1 = planets.begin(); planets.end() != litp1; litp1++)
		{
			spacePlanetObject * planet = (*litp1);
			if (((!(actx->startingPlanet              )) ||
				 (!(rollADice(FIRST_GASS_TITAN_CHANCE))))
				 &&
				 (!gassTitansOnly))
			{
				planet->setPlanetToBeTerestrial();
			}
			else
			{
				planet->setPlanetToBeGassTitan();
				gassTitansOnly = 1;
			}
			if (GAS_TITAN == planet->getPlanetType())
			{
				double planetCapacity = planet->getWeight() / max(GAS_TITAN_MIN_DENSITY, dRandomNumberFromScale(GAS_TITAN_MIN_DENSITY, GAS_TITAN_MAX_DENSITY, 2));
				planet->setObjectRadius(pow((double)(planetCapacity / ((double)4/(double)3 * PI)), (double)1 / (double)3) / KILO);
			}
			else
			{
				double planetCapacity = planet->getWeight() / max(TERESTIC_MAX_DENSITY, dRandomNumberFromScale(TERESTIC_MIN_DENSITY, TERESTIC_MAX_DENSITY, 2));
				planet->setObjectRadius(pow((double)(planetCapacity / ((double)4/(double)3 * PI)), (double)1 / (double)3) / KILO);
			}
			planet->setAreaRadius(planet->getWeight() * AREA_RADIUS_TO_CENTER_WEIGHT_IN_KM);
			innerCircleEdge      = outerCircleEdge;
			outerCircleEdge     += max(planet->getWeight() / weightTotal * solarDiscWidth, 2 * SPACE_OBJECT_DISTANCE_SPACING * planet->getAreaRadius());
			double planetCircle  = dRandomNumberFromScale(innerCircleEdge, outerCircleEdge, 2);
			if ((!(actx->startingPlanet)) &&
				(loop1 <= EARTH_LOOP1   ))
			{
				if (MERCURY_LOOP1 == loop1)
				{
					planetCircle = MERCURY_CIRCLE;
				}
				if (VENUS_LOOP1 == loop1)
				{
					planetCircle = VENUS_CIRCLE;
				}
				if (EARTH_LOOP1 == loop1)
				{
					planetCircle = EARTH_CIRCLE;
				}
				innerCircleEdge = planetCircle - planet->getAreaRadius();
				outerCircleEdge = planetCircle + planet->getAreaRadius();
			}
			else
			{
				if (outerCircleEdge < planetCircle + planet->getAreaRadius())
				{
					planetCircle = outerCircleEdge - planet->getAreaRadius();
				}
				if (planetCircle - planet->getAreaRadius() < innerCircleEdge)
				{
					planetCircle = innerCircleEdge + planet->getAreaRadius();
				}
			}
			planet->setDistanceFromParent(planetCircle);
			planet->setBasicRotationAngle(dRandomNumberFromScale(0, 360, 1));
			planet->setAngularVelocity((180 * star->getOrbitingSpeed(planet->getDistanceFromParentObject()) / KILO) / (PI * planet->getDistanceFromParentObject()) / NANO/*pow(physicsConstants<double>::kappa() * star->getWeight() / (planet->getDistanceFromParentObject() * KILO), (double)1 / (double)2) / (planet->getDistanceFromParentObject() * KILO)*/);
			planet->setYearPeriod(ullRound((double)360 / max(NANO, planet->getAngularVelocity() * NANO)));
			if (TERESTRIAL == planet->getPlanetType())
			{
				planet->setDayPeriod(ullRound(dRandomNumberFromScale(MIN_DAY_IN_SECONDS, MAX_DAY_IN_SECONDS, 1)));
			}
			else
			{
				planet->setDayPeriod(ullRound(dRandomNumberFromScale(MIN_DAY_IN_SECONDS, MAX_GAS_DAY_IN_SECONDS, 2)));
			}
			double temperatureScale = (solarSystemRadius - planet->getDistanceFromParentObject()) / solarSystemRadius;
			planet->setDayTemperature(lNumberFromScale(MIN_TEMPERATURE, MAX_TEMPERATURE, (double)1, pow(temperatureScale, (double)2)));
			if (dayTemp < planet->getDayTemperature())
			{
				planet->setDayTemperature(lNumberFromScale(MIN_TEMPERATURE, dayTemp, (double)1, temperatureScale));
			}
			if ((!(actx->startingPlanet)) &&
				(EARTH_LOOP1 == loop1   ))
			{
				planet->initStartingPlanet();
				actx->startingPlanet = planet;
			}
			else
			{
				if (!(actx->startingPlanet))
				{
					if (MERCURY_LOOP1 == loop1)
					{
						planet->setDayTemperature(444);
					}
					if (VENUS_LOOP1 == loop1)
					{
						planet->setDayTemperature(402);
					}
				}
				planet->initPlanetByTypeAndDayTemperature();
			}
			dayTemp = planet->getDayTemperature();

			loop1++;
		}
	}
}

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

void spacePlanetObject::initPlanetByTypeAndDayTemperature()
{
	this->nightTemperature_ = MIN_TEMPERATURE + rollA0ToNSidedDice(this->dayTemperature_ - MIN_TEMPERATURE);

	if ((MIN_TEMPERATURE_FOR_SEA > this->dayTemperature_  ) ||
		(MAX_TEMPERATURE_FOR_SEA < this->nightTemperature_))
	{
		this->sea_ = 0;
	}
	else
	{
		if (rollADice(WHOLE_SEA_CHANCE))
		{
			this->sea_ = (long)SOURCES_MODIFIER;
		}
		else
		{
			this->sea_ = (long)rollANSidedDice((unsigned long)SOURCES_MODIFIER);
		}
	}

	unsigned long HeWMax = 0;
	unsigned long ZrCMax = 0;
	unsigned long IlmMax = 0;
	if (this->sea_ < ZRC_HEW_SEA_MAX_LIMIT)
	{
		HeWMax = (unsigned long)this->sea_;
		ZrCMax = (unsigned long)this->sea_;
	}
	else
	{
		HeWMax = (unsigned long)THE_BEST_SOURCE;
		ZrCMax = (unsigned long)THE_BEST_SOURCE;
	}
	if (ulSafeSub((unsigned long)SOURCES_MODIFIER, (unsigned long)this->sea_) < (unsigned long)ILM_TERRA_MAX_LIMIT)
	{
		IlmMax = ulSafeSub((unsigned long)SOURCES_MODIFIER, (unsigned long)this->sea_);
	}
	else
	{
		IlmMax = (unsigned long)THE_BEST_SOURCE;
	}
	if (GAS_TITAN == this->getPlanetType())
	{
		IlmMax = min(IlmMax, (unsigned long)ILM_GAS_MAX_LIMIT);
	}

	this->heavyWaterSource_ = rollA0ToNSidedDice(HeWMax);
	this->zirconiumSource_  = rollA0ToNSidedDice(ZrCMax);
	this->ilmeniteSource_   = rollA0ToNSidedDice(IlmMax);
}

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

void spacePlanetObject::initStartingPlanet()
{
	// parametry Zeme, netreba konstant, nezajimave, navic herne nedulezite
	this->type_             = SOT_PLANET;
	this->yearPeriod_       = 31556926;
	this->dayPeriod_        = 86164;
	this->planetType_       = TERESTRIAL;
	this->dayTemperature_   = 22;
	this->nightTemperature_ = 16;
	this->sea_              = 7080;
	this->heavyWaterSource_ = (unsigned long)SOURCES_MODIFIER;
	this->zirconiumSource_  = (unsigned long)SOURCES_MODIFIER;
	this->ilmeniteSource_   = (unsigned long)SOURCES_MODIFIER;
}

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

void spaceFortressObject :: getAimedTargets(std :: list<dtIdentifier>& ids)
{
    baseClass* bc = actx->cacheFortressesList.getItem<baseClass>(idFortress_);
    if (bc != NULL)
    {
        dtIdentifier target;
        if ((target = bc->getWeaponsTargetID()) != NOT_VALID_ID)
        {
            ids.push_back(target);
        }
    }
    else
    {
        errLogicalError("Base with id %u not found!", (unsigned long long)idFortress_);
    }
}

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

void spaceRegularObject :: getAimedTargets(std :: list<dtIdentifier>& ids)
{
    for (dtIdentifierConstIterator iter = subObjectsBegin(); iter != subObjectsEnd(); iter++)
    {
        spaceObject* obj = actx->cacheSpaceObjectList.getItem<spaceObject>(*iter);
        if (obj != NULL)
        {
            obj->getAimedTargets(ids);
        }
        else
        {
            errLogicalError("Space object with id %u not found!", (unsigned long long)(*iter));
        }
    }
}

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