// ################################
// ##  Tridy , ktere tvori vedu  ##
// ##   ne konkretni instance    ##
// ################################

#include <cmath>

#include "science_tree.h"

using namespace std;

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

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

treeLeaf::treeLeaf(entitiesSupportedByScience leafEntity, size_t entityIndexesEnd)
{
	ascendant_      = NULL; 
	assignedPts_    = 0;
	entity_         = leafEntity; 
	freePtsInNode_  = 0;
	itemComponents_.resize(entityIndexesEnd);
	fillContainerWithValue<unsignedShortDoublePairVector, unsignedShortDoublePair>(itemComponents_, make_pair<unsigned short, double>((unsigned short)DEFAULT_SPEC_PTS_FOR_FULL_USAGE, (double)0));
};

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

unsigned short treeLeaf::getSpecialisationPts(size_t specialisationIndex)
{
	if (!(specialisationIndex < itemComponents_.size()))
	{
        errLogicalError("Entity %u, specialization %u: bad index in science tree leaf",(unsigned long long)entity_,(unsigned long long)specialisationIndex);
		return 0;
	}

	return itemComponents_[specialisationIndex].first;
};

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

unsigned short treeLeaf::setSpecialisationPts(size_t specialisationIndex, unsigned short newPts)
{
	if (!(specialisationIndex < itemComponents_.size()))
	{
		errLogicalError("Entity %u, specialization %u: bad index in science tree leaf",(unsigned long long)entity_,(unsigned long long)specialisationIndex);
		return 0;
	}

	if (newPts < itemComponents_[specialisationIndex].first)
	{
		return subSpecialisationPts(specialisationIndex, itemComponents_[specialisationIndex].first - newPts);
	}
	if (newPts > itemComponents_[specialisationIndex].first)
	{
		return addSpecialisationPts(specialisationIndex, newPts - itemComponents_[specialisationIndex].first);
	}

	return 0;
};

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

unsigned short treeLeaf::addSpecialisationPts(size_t specialisationIndex, unsigned short morePts)
{
	if (!(specialisationIndex < itemComponents_.size()))
	{
		errLogicalError("Entity %u, specialization %u: bad index in science tree leaf",(unsigned long long)entity_,(unsigned long long)specialisationIndex);
		return 0;
	}

	morePts                                     = min(morePts, freePtsInNode_);
	freePtsInNode_                             -= morePts;
	itemComponents_[specialisationIndex].first += morePts;

	return morePts;
};

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

unsigned short treeLeaf::subSpecialisationPts(size_t specialisationIndex, unsigned short lessPts)
{
	if (!(specialisationIndex < itemComponents_.size()))
	{
		errLogicalError("Entity %u, specialization %u: bad index in science tree leaf",(unsigned long long)entity_,(unsigned long long)specialisationIndex);
		return 0;
	}

	lessPts                                     = min(lessPts, itemComponents_[specialisationIndex].first);
	itemComponents_[specialisationIndex].first -= lessPts;
	freePtsInNode_                             += lessPts;

	return lessPts;
};

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

unsigned short treeLeaf::collectAllPtsUptoRoot()
{
	treeNode * greyNode = this;				
	double multiplier  = 1;
	double ptsCounter  = (greyNode->getPts() * multiplier);

	if (NULL == greyNode->getAscendant())
	{
        errLogicalError("Entity %u: null items group",(unsigned long long)((treeLeaf*)greyNode)->getEntity());
		return 0;
	}
	else
	{
		greyNode = greyNode->getAscendant();
	}
	multiplier *= GROUPS_TO_ITEMS_MULTIPLIER;
	ptsCounter += (greyNode->getPts() * multiplier);

	if (NULL == greyNode->getAscendant())
	{
        errLogicalError("Entity %u: null groups category",(unsigned long long)((treeInnerNode*)greyNode)->getSupremeEntity());
		return 0;
	}
	else
	{
		greyNode = greyNode->getAscendant();
	}
	multiplier *= CATEGORIES_TO_GROUPS_MULTIPLIER;
	ptsCounter += (greyNode->getPts() * multiplier);

	if (NULL == greyNode->getAscendant())
	{
        errLogicalError("Supreme entity %u: null science",(unsigned long long)((treeInnerNode*)greyNode)->getSupremeEntity());
		return 0;
	}
	else
	{
		greyNode = greyNode->getAscendant();
	}
	multiplier *= ROOT_TO_CATEGORIES_MULTIPLIER;
	ptsCounter += (greyNode->getPts() * multiplier);

	return (unsigned short)ulRound(ptsCounter);
};

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

double treeLeaf::getRealScientificLevel(size_t specialisationIndex)
{
	if (!(specialisationIndex < itemComponents_.size()))
	{
        errLogicalError("Entity %u, specialization %u: bad index in science tree leaf",(unsigned long long)entity_,(unsigned long long)specialisationIndex);
		return 0;
	}

	return itemComponents_[specialisationIndex].second;
};

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

void treeLeaf::increaseScientificLevelsByScientificPoints(unsigned long long producedScientificPoints)
{
	for (unsignedShortDoublePairVector::iterator litp1 = this->itemComponents_.begin(); this->itemComponents_.end() != litp1; litp1++)
	{
		double moreSpecificPoints = (double)litp1->first                  / dHandleZero(HUNDRED_PERCENT             ) *
			                        (double)this->collectAllPtsUptoRoot() / dHandleZero(NORMALIZED_LEAF_TOTAL_TOKENS) * 
									(double)producedScientificPoints                                                  ;
		double actualLevel        = litp1->second;
		double actualExps         = pow(actualLevel, LEVEL_TO_SCIENTIFIC_POINTS_EXPONENT);
		double newExps            = actualExps + moreSpecificPoints;
		double newLevel           = pow(newExps, (double)1 / dHandleZero(LEVEL_TO_SCIENTIFIC_POINTS_EXPONENT));
		newLevel                  = max((double)0, min((double)MAX_TOTAL_SCIENCE_LEVEL, newLevel));
		litp1->second             = newLevel;
	}
};

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

void treeInnerNode :: markNodeIndex(unsigned short& currentIndex)
{
	for ( treeNodePVector :: iterator iter = descendants_.begin(); iter != descendants_.end(); iter++)
	{
		(*iter)->markNodeIndex(currentIndex);
	}

	nodeIndex_ = ++currentIndex;
}

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

treeInnerNode::treeInnerNode(supremeEntitiesSupportedByScience innerNodeEntity)
{
	ascendant_     = NULL; 
	assignedPts_   = 0;
	supremeEntity_ = innerNodeEntity; 
	descendants_.clear();
}

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

scientificTree::scientificTree()
{
	quarkGluonCore_               = new treeLeaf(SCIENCE_ENTITY_QUARK_GLUON_CORE               , INDEX_QUARK_GLUON_CORE_END               );
	navigationComputer_           = new treeLeaf(SCIENCE_ENTITY_NAVIGATION_COMPUTER            , INDEX_NAVIGATION_COMPUTER_END            );
	subspaceBeacon_               = new treeLeaf(SCIENCE_ENTITY_SUBSPACE_BEACON                , INDEX_SUBSPACE_BEACON_END                );
	inertialDampers_              = new treeLeaf(SCIENCE_ENTITY_INERTIAL_DAMPERS               , INDEX_INERTIAL_DAMPERS_END               );

	impulsJetEngine_              = new treeLeaf(SCIENCE_ENTITY_IMPULS_JET_ENGINE              , INDEX_IMPULS_ENGINE_END                  );
	mechanicalDampers_            = new treeLeaf(SCIENCE_ENTITY_MECHANICAL_DAMPERS             , INDEX_MECHANICAL_DAMPERS_END             );
	droneMissiles_                = new treeLeaf(SCIENCE_ENTITY_DRONE_MISSILES                 , INDEX_DRONE_MISSILES_END                 );
	droneMissileLauncher_         = new treeLeaf(SCIENCE_ENTITY_DRONE_MISSILE_LAUNCHER         , INDEX_DRONE_MISSILE_LAUNCHER_END         );

	shipsConstruction_            = new treeLeaf(SCIENCE_ENTITY_SHIPS                          , INDEX_SHIPS_END                          );
	basesConstruction_            = new treeLeaf(SCIENCE_ENTITY_BASES                          , INDEX_BASES_END                          );
	hangar_                       = new treeLeaf(SCIENCE_ENTITY_HANGAR                         , INDEX_HANGARS_END                        );
	androids_                     = new treeLeaf(SCIENCE_ENTITY_ANDROIDS                       , INDEX_ANDROIDS_END                       );

	energeticAccumulator_         = new treeLeaf(SCIENCE_ENTITY_ENERGETIC_ACCUMULATOR          , INDEX_ENERGETIC_ACCUMULATOR_END          );
	mainframe_                    = new treeLeaf(SCIENCE_ENTITY_MAINFRAME                      , INDEX_MAINFRAME_END                      );
	scientificLab_                = new treeLeaf(SCIENCE_ENTITY_SCIENTIFIC_LAB                 , INDEX_SCIENTIFIC_LABS_END                );
	lifeSupport_                  = new treeLeaf(SCIENCE_ENTITY_LIFE_SUPPORT                   , INDEX_LIFE_SUPPORT_END                   );

	weaponEnergyCircuit_          = new treeLeaf(SCIENCE_ENTITY_WEAPON_ENERGY_CIRCUIT          , INDEX_WEAPON_ENERGY_CIRCUIT_END          );
	transportBeam_                = new treeLeaf(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR       , INDEX_TRANSPORT_BEAM_GENERATOR_END       );
	laserGun_                     = new treeLeaf(SCIENCE_ENTITY_LASER_GUN                      , INDEX_LASER_GUN_END                      );
	deflectorShieldsGenerator_    = new treeLeaf(SCIENCE_ENTITY_DEFLECTOR_SHIELDS              , INDEX_DEFLECTOR_SHIELDS_GENERATOR_END    );

	highResolutionSensors_        = new treeLeaf(SCIENCE_ENTITY_HIGH_RESOLUTION_SENSORS        , INDEX_HIGH_RESOLUTION_SENSORS_END        );
	longRangeSensors_             = new treeLeaf(SCIENCE_ENTITY_LONG_RANGE_SENSORS             , INDEX_LONG_RANGE_SENSORS_END             );
	cloakingDevice_               = new treeLeaf(SCIENCE_ENTITY_CLOAKING_DEVICE                , INDEX_CLOAKING_DEVICE_END                );
	probe_                        = new treeLeaf(SCIENCE_ENTITY_PROBE                          , INDEX_PROBE_DEVICE_END                   );

	magneticalFilter_             = new treeLeaf(SCIENCE_ENTITY_MAGNETICAL_FILTER              , INDEX_MAGNETICAL_FILTER_END              );
	deuteriumDecomposer_          = new treeLeaf(SCIENCE_ENTITY_DEUTERIUM_DECOMPOSER           , INDEX_DEUTERIUM_DECOMPOSER_END           );
	zirconiumReactor_             = new treeLeaf(SCIENCE_ENTITY_ZIRCONIUM_REACTOR              , INDEX_ZIRCONIUM_REACTOR_END              );
	ilmeniteFurnace_              = new treeLeaf(SCIENCE_ENTITY_ILMENITE_FURNACE               , INDEX_ILMENITE_FURNACE_END               );

	magneticalCollectorsOfPlasma_ = new treeLeaf(SCIENCE_ENTITY_MAGNETICAL_COLLECTORS_OF_PLASMA, INDEX_MAGNETICAL_COLLECTORS_OF_PLASMA_END);
	submersibleFiltrationTank_    = new treeLeaf(SCIENCE_ENTITY_SUBMERSIBLE_FILTRATION_TANK    , INDEX_SUBMERSIBLE_FILTRATION_TANK_END    );
	submarineMiningMachine_       = new treeLeaf(SCIENCE_ENTITY_SUBMARINE_MINING_MACHINE       , INDEX_SUBMARINE_MINING_MACHINE_END       );
	undergroundDrillingMachine_   = new treeLeaf(SCIENCE_ENTITY_UNDERGROUND_DRILLING_MACHINE   , INDEX_UNDERGROUND_DRILLING_MACHINE_END   );


	subspaceDrive_      = new treeInnerNode(SCIENCE_SUPREME_ENTITY_SUBSPACE_DRIVE     );
	subspaceDrive_->addDescendant(quarkGluonCore_    );
	subspaceDrive_->addDescendant(navigationComputer_);
	subspaceDrive_->addDescendant(subspaceBeacon_    );
	subspaceDrive_->addDescendant(inertialDampers_   );
	impulsDrive_        = new treeInnerNode(SCIENCE_SUPREME_ENTITY_IMPULS_DRIVE       );
	impulsDrive_->addDescendant(impulsJetEngine_     );
	impulsDrive_->addDescendant(mechanicalDampers_   );
	impulsDrive_->addDescendant(droneMissiles_       );
	impulsDrive_->addDescendant(droneMissileLauncher_);

	construction_       = new treeInnerNode(SCIENCE_SUPREME_ENTITY_CONSTRUCTION       );
	construction_->addDescendant(shipsConstruction_);
	construction_->addDescendant(basesConstruction_);
	construction_->addDescendant(hangar_           );
	construction_->addDescendant(androids_         );
	equipment_          = new treeInnerNode(SCIENCE_SUPREME_ENTITY_EQUIPMENT          );
	equipment_->addDescendant(energeticAccumulator_);
	equipment_->addDescendant(mainframe_           );
	equipment_->addDescendant(scientificLab_       );
	equipment_->addDescendant(lifeSupport_         );

	energeticRig_       = new treeInnerNode(SCIENCE_SUPREME_ENTITY_ENERGETIC_RING      );
	energeticRig_->addDescendant(weaponEnergyCircuit_      );
	energeticRig_->addDescendant(transportBeam_            );
	energeticRig_->addDescendant(laserGun_                 );
	energeticRig_->addDescendant(deflectorShieldsGenerator_);
	recognitionDevices_ = new treeInnerNode(SCIENCE_SUPREME_ENTITY_RECOGNITION_DEVICES);
	recognitionDevices_->addDescendant(highResolutionSensors_);
	recognitionDevices_->addDescendant(longRangeSensors_     );
	recognitionDevices_->addDescendant(cloakingDevice_       );
	recognitionDevices_->addDescendant(probe_                );

	factories_          = new treeInnerNode(SCIENCE_SUPREME_ENTITY_FACTORIES          );
	factories_->addDescendant(magneticalFilter_   );
	factories_->addDescendant(deuteriumDecomposer_);
	factories_->addDescendant(zirconiumReactor_   );
	factories_->addDescendant(ilmeniteFurnace_    );
	quarryingMachines_  = new treeInnerNode(SCIENCE_SUPREME_ENTITY_QUARRYING_MACHINES );
	factories_->addDescendant(magneticalCollectorsOfPlasma_);
	factories_->addDescendant(submersibleFiltrationTank_   );
	factories_->addDescendant(submarineMiningMachine_      );
	factories_->addDescendant(undergroundDrillingMachine_  );


	drive_      = new treeInnerNode(SCIENCE_SUPREME_ENTITY_DRIVE     );
	drive_->addDescendant(subspaceDrive_);
	drive_->addDescendant(impulsDrive_  );
	platform_   = new treeInnerNode(SCIENCE_SUPREME_ENTITY_PLATFORM  );
	platform_->addDescendant(construction_);
	platform_->addDescendant(equipment_   );
	modules_    = new treeInnerNode(SCIENCE_SUPREME_ENTITY_MODULES   );
	modules_->addDescendant(energeticRig_      );
	modules_->addDescendant(recognitionDevices_);
	production_ = new treeInnerNode(SCIENCE_SUPREME_ENTITY_PRODUCTION);
	production_->addDescendant(factories_        );
	production_->addDescendant(quarryingMachines_);


	scienceTotal_ = new treeInnerNode(SCIENCE_SUPREME_ENTITY_SCIENCE);
	scienceTotal_->addDescendant(drive_     );
	scienceTotal_->addDescendant(platform_  );
	scienceTotal_->addDescendant(modules_   );
	scienceTotal_->addDescendant(production_);


	scienceTotal_->setPts(TOKENS_TOTAL_COUNT);
	freeTokens_ = 0;

	// nastavime indexy uzlu
	unsigned short nodeIdx = 0;
	scienceTotal_->markNodeIndex(nodeIdx);
}

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

scientificTree::~scientificTree()
{
	delete quarkGluonCore_;
	delete navigationComputer_;
	delete subspaceBeacon_;
	delete inertialDampers_;

	delete impulsJetEngine_;
	delete mechanicalDampers_;
	delete droneMissiles_;
	delete droneMissileLauncher_;

	delete shipsConstruction_;
	delete basesConstruction_;
	delete hangar_;
	delete androids_;

	delete energeticAccumulator_;
	delete mainframe_;
	delete scientificLab_;
	delete lifeSupport_;

	delete weaponEnergyCircuit_;
	delete transportBeam_;
	delete laserGun_;
	delete deflectorShieldsGenerator_;

	delete highResolutionSensors_;
	delete longRangeSensors_;
	delete cloakingDevice_;
	delete probe_;

	delete magneticalFilter_;
	delete deuteriumDecomposer_;
	delete zirconiumReactor_;
	delete ilmeniteFurnace_;

	delete magneticalCollectorsOfPlasma_;
	delete submersibleFiltrationTank_;
	delete submarineMiningMachine_;
	delete undergroundDrillingMachine_;


	delete subspaceDrive_;
	delete impulsDrive_;

	delete construction_;
	delete equipment_;

	delete energeticRig_;
	delete recognitionDevices_;

	delete factories_;
	delete quarryingMachines_;


	delete drive_;
	delete platform_;
	delete modules_;
	delete production_;


	delete scienceTotal_;
}

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

treeLeaf * scientificTree::getTreeLeafPtr(entitiesSupportedByScience leafEntity)
{
	switch (leafEntity)
	{
	case SCIENCE_ENTITY_SHIPS:                           return shipsConstruction_;            break;
	case SCIENCE_ENTITY_BASES:                           return basesConstruction_;            break;
	case SCIENCE_ENTITY_ENERGETIC_ACCUMULATOR:           return energeticAccumulator_;         break;
	case SCIENCE_ENTITY_NAVIGATION_COMPUTER:             return navigationComputer_;           break;
	case SCIENCE_ENTITY_QUARK_GLUON_CORE:                return quarkGluonCore_;               break;
	case SCIENCE_ENTITY_SUBSPACE_BEACON:                 return subspaceBeacon_;               break;
	case SCIENCE_ENTITY_INERTIAL_DAMPERS:                return inertialDampers_;              break;
	case SCIENCE_ENTITY_IMPULS_JET_ENGINE:               return impulsJetEngine_;              break;
	case SCIENCE_ENTITY_MECHANICAL_DAMPERS:              return mechanicalDampers_;            break;
	case SCIENCE_ENTITY_DEFLECTOR_SHIELDS:               return deflectorShieldsGenerator_;    break;
	case SCIENCE_ENTITY_WEAPON_ENERGY_CIRCUIT:           return weaponEnergyCircuit_;          break;
	case SCIENCE_ENTITY_DRONE_MISSILE_LAUNCHER:          return droneMissileLauncher_;         break;
	case SCIENCE_ENTITY_DRONE_MISSILES:                  return droneMissiles_;                break;
	case SCIENCE_ENTITY_LASER_GUN:                       return laserGun_;                     break;
	case SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR:        return transportBeam_;                break;
	case SCIENCE_ENTITY_MAGNETICAL_COLLECTORS_OF_PLASMA: return magneticalCollectorsOfPlasma_; break;
	case SCIENCE_ENTITY_SUBMERSIBLE_FILTRATION_TANK:     return submersibleFiltrationTank_;    break;
	case SCIENCE_ENTITY_SUBMARINE_MINING_MACHINE:        return submarineMiningMachine_;       break;
	case SCIENCE_ENTITY_UNDERGROUND_DRILLING_MACHINE:    return undergroundDrillingMachine_;   break;
	case SCIENCE_ENTITY_PROBE:                           return probe_;                        break;
	case SCIENCE_ENTITY_HIGH_RESOLUTION_SENSORS:         return highResolutionSensors_;        break;
	case SCIENCE_ENTITY_LONG_RANGE_SENSORS:              return longRangeSensors_;             break;
	case SCIENCE_ENTITY_CLOAKING_DEVICE:                 return cloakingDevice_;               break;
	case SCIENCE_ENTITY_ANDROIDS:                        return androids_;                     break;
	case SCIENCE_ENTITY_SCIENTIFIC_LAB:                  return scientificLab_;                break;
	case SCIENCE_ENTITY_LIFE_SUPPORT:                    return lifeSupport_;                  break;
	case SCIENCE_ENTITY_MAINFRAME:                       return mainframe_;                    break;
	case SCIENCE_ENTITY_HANGAR:                          return hangar_;                       break;
	case SCIENCE_ENTITY_MAGNETICAL_FILTER:               return magneticalFilter_;             break;
	case SCIENCE_ENTITY_DEUTERIUM_DECOMPOSER:            return deuteriumDecomposer_;          break;
	case SCIENCE_ENTITY_ZIRCONIUM_REACTOR:               return zirconiumReactor_;             break;
	case SCIENCE_ENTITY_ILMENITE_FURNACE:                return ilmeniteFurnace_;              break;

	default:
		errLogicalError("Leaf entity %u not in enum",(unsigned long long)leafEntity);
		return NULL;
		break;
	}
}

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

treeInnerNode * scientificTree::getTreeInnerNodePtr(supremeEntitiesSupportedByScience innerEntity)
{
	switch (innerEntity)
	{
	case SCIENCE_SUPREME_ENTITY_SCIENCE:             return scienceTotal_;       break;

	case SCIENCE_SUPREME_ENTITY_DRIVE:               return drive_;              break;
	case SCIENCE_SUPREME_ENTITY_PLATFORM:            return platform_;           break;
	case SCIENCE_SUPREME_ENTITY_MODULES:             return modules_;            break;
	case SCIENCE_SUPREME_ENTITY_PRODUCTION:          return production_;         break;

	case SCIENCE_SUPREME_ENTITY_SUBSPACE_DRIVE:      return subspaceDrive_;      break;
	case SCIENCE_SUPREME_ENTITY_IMPULS_DRIVE:        return impulsDrive_;        break;
	case SCIENCE_SUPREME_ENTITY_CONSTRUCTION:        return construction_;       break;
	case SCIENCE_SUPREME_ENTITY_EQUIPMENT:           return equipment_;          break;
	case SCIENCE_SUPREME_ENTITY_ENERGETIC_RING:       return energeticRig_;       break;
	case SCIENCE_SUPREME_ENTITY_RECOGNITION_DEVICES: return recognitionDevices_; break;
	case SCIENCE_SUPREME_ENTITY_FACTORIES:           return factories_;          break;
	case SCIENCE_SUPREME_ENTITY_QUARRYING_MACHINES:  return quarryingMachines_;  break;

	default:
		errLogicalError("Inner entity %u not in enum",(unsigned long long)innerEntity);
		return NULL;
		break;
	}
}

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

unsigned short scientificTree::getAllsLeafTokens(entitiesSupportedByScience leafEntity)
{
	return (getTreeLeafPtr(leafEntity))->collectAllPtsUptoRoot();
}

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

unsigned short scientificTree::getPureLeafTokens(entitiesSupportedByScience leafEntity)
{
	return (getTreeLeafPtr(leafEntity))->getPts();
}

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

unsigned short scientificTree::setPureLeafTokens(entitiesSupportedByScience leafEntity, unsigned short newTokens)
{
	treeLeaf * entityLeafPtr = getTreeLeafPtr(leafEntity);

	if (newTokens < entityLeafPtr->getPts())
	{
		return subPureLeafTokens(leafEntity, entityLeafPtr->getPts() - newTokens);
	}
	if (newTokens > entityLeafPtr->getPts())
	{
		return addPureLeafTokens(leafEntity, newTokens - entityLeafPtr->getPts());
	}

	return 0;
}

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

unsigned short scientificTree::addPureLeafTokens(entitiesSupportedByScience leafEntity, unsigned short moreTokens)
{
	moreTokens = min(moreTokens, freeTokens_);
	if (0 < moreTokens)
	{
		freeTokens_ -= moreTokens;
		return (getTreeLeafPtr(leafEntity))->addPts(moreTokens);
	}
	else
	{
		return 0;
	}
}

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

unsigned short scientificTree::subPureLeafTokens(entitiesSupportedByScience leafEntity, unsigned short lessTokens)
{
	treeLeaf * entityLeafPtr = getTreeLeafPtr(leafEntity);
	lessTokens               = min(lessTokens, entityLeafPtr->getPts());
	if (0 < lessTokens)
	{
		lessTokens   = entityLeafPtr->subPts(lessTokens);
		freeTokens_ += lessTokens;
		return lessTokens;
	}
	else
	{
		return 0;
	}
}

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

unsigned short scientificTree::getSpecLeafTokens(entitiesSupportedByScience leafEntity, size_t index)
{
	return (getTreeLeafPtr(leafEntity))->getSpecialisationPts(index);
}

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

unsigned short scientificTree::setSpecLeafTokens(entitiesSupportedByScience leafEntity, size_t index, unsigned short newTokens)
{
	return (getTreeLeafPtr(leafEntity))->setSpecialisationPts(index, newTokens);
}

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

unsigned short scientificTree::addSpecLeafTokens(entitiesSupportedByScience leafEntity, size_t index, unsigned short moreTokens)
{
	return (getTreeLeafPtr(leafEntity))->addSpecialisationPts(index, moreTokens);
}

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

unsigned short scientificTree::subSpecLeafTokens(entitiesSupportedByScience leafEntity, size_t index, unsigned short lessTokens)
{
	return (getTreeLeafPtr(leafEntity))->subSpecialisationPts(index, lessTokens);
}

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

unsigned short scientificTree::getPureInnerTokens(supremeEntitiesSupportedByScience innerEntity)
{
	return (getTreeInnerNodePtr(innerEntity))->getPts();
}

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

unsigned short scientificTree::setPureInnerTokens(supremeEntitiesSupportedByScience innerEntity, unsigned short newTokens)
{
	treeInnerNode * innerEntityPtr = getTreeInnerNodePtr(innerEntity);

	if (newTokens < innerEntityPtr->getPts())
	{
		return subPureInnerTokens(innerEntity, innerEntityPtr->getPts() - newTokens);
	}
	if (newTokens > innerEntityPtr->getPts())
	{
		return addPureInnerTokens(innerEntity, newTokens - innerEntityPtr->getPts());
	}

	return 0;
}

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

unsigned short scientificTree::addPureInnerTokens(supremeEntitiesSupportedByScience innerEntity, unsigned short moreTokens)
{
	moreTokens = min(moreTokens, freeTokens_);
	if (0 < moreTokens)
	{
		freeTokens_ -= moreTokens;
		return (getTreeInnerNodePtr(innerEntity))->addPts(moreTokens);
	}
	else
	{
		return 0;
	}
}

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

unsigned short scientificTree::subPureInnerTokens(supremeEntitiesSupportedByScience innerEntity, unsigned short lessTokens)
{
	treeInnerNode * innerEntityPtr = getTreeInnerNodePtr(innerEntity);
	lessTokens                     = min(lessTokens, innerEntityPtr->getPts());
	if (0 < lessTokens)
	{
		lessTokens   = innerEntityPtr->subPts(lessTokens);
		freeTokens_ += lessTokens;
		return lessTokens;
	}
	else
	{
		return 0;
	}
}

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

double scientificTree::getRealScienceLevel(entitiesSupportedByScience leafEntity, size_t index)
{
	return floor((getTreeLeafPtr(leafEntity))->getRealScientificLevel(index));
}

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

void scientificTree::distributeScientificPointsToLeafs(unsigned long long moreScientificPoints)
{
	this->quarkGluonCore_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->navigationComputer_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->subspaceBeacon_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->inertialDampers_->increaseScientificLevelsByScientificPoints(moreScientificPoints);

	this->impulsJetEngine_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->mechanicalDampers_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->droneMissiles_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->droneMissileLauncher_->increaseScientificLevelsByScientificPoints(moreScientificPoints);

	this->shipsConstruction_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->basesConstruction_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->hangar_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->androids_->increaseScientificLevelsByScientificPoints(moreScientificPoints);

	this->energeticAccumulator_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->mainframe_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->scientificLab_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->lifeSupport_->increaseScientificLevelsByScientificPoints(moreScientificPoints);

	this->weaponEnergyCircuit_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->transportBeam_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->laserGun_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->deflectorShieldsGenerator_->increaseScientificLevelsByScientificPoints(moreScientificPoints);

	this->highResolutionSensors_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->longRangeSensors_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->cloakingDevice_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->probe_->increaseScientificLevelsByScientificPoints(moreScientificPoints);

	this->magneticalFilter_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->deuteriumDecomposer_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->zirconiumReactor_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->ilmeniteFurnace_->increaseScientificLevelsByScientificPoints(moreScientificPoints);

	this->magneticalCollectorsOfPlasma_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->submersibleFiltrationTank_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->submarineMiningMachine_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
	this->undergroundDrillingMachine_->increaseScientificLevelsByScientificPoints(moreScientificPoints);
}

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

void scientificTree::setWholeTreeLevelForNewPlayer(double newScientificLevel)
{
	newScientificLevel = max((double)0, min((double)MAX_TOTAL_SCIENCE_LEVEL, newScientificLevel));
	double newExps = pow(newScientificLevel, LEVEL_TO_SCIENTIFIC_POINTS_EXPONENT);
	this->distributeScientificPointsToLeafs(ullUpRound(newExps));
}

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

unsigned short scientificTree::totalTokens()
{
    return TOKENS_TOTAL_COUNT;
}

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

void scientificTree::nullifyNode(treeNode* node)
{
    if (node->isInner())
    {       
        treeInnerNode* inner = (treeInnerNode*)(node);
        setPureInnerTokens(inner->getSupremeEntity(), 0);
        for (treeNodePVector :: iterator iter = inner->descendants_.begin(); iter != inner->descendants_.end(); inner++)
        {
            nullifyNode(*iter);
        }
    }
    else
    {
        treeLeaf* leaf = (treeLeaf*)(node);
        setPureLeafTokens(leaf->getEntity(), 0);
        for (unsigned int i = 0; i < leaf->itemComponents_.size(); i++)
        {
            setSpecLeafTokens(leaf->getEntity(), i, 0);
        }
    }
}

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

unsigned short scientificTree::nullifyTreeTokens(void) 
{
    setPureLeafTokens(SCIENCE_ENTITY_SHIPS,                             0);
    setPureLeafTokens(SCIENCE_ENTITY_BASES,                             0);
    setPureLeafTokens(SCIENCE_ENTITY_ENERGETIC_ACCUMULATOR,             0);
    setPureLeafTokens(SCIENCE_ENTITY_NAVIGATION_COMPUTER,               0);
    setPureLeafTokens(SCIENCE_ENTITY_QUARK_GLUON_CORE,                  0);
    setPureLeafTokens(SCIENCE_ENTITY_SUBSPACE_BEACON,                   0);
    setPureLeafTokens(SCIENCE_ENTITY_INERTIAL_DAMPERS,                  0);
    setPureLeafTokens(SCIENCE_ENTITY_IMPULS_JET_ENGINE,                 0);
    setPureLeafTokens(SCIENCE_ENTITY_MECHANICAL_DAMPERS,                0);
    setPureLeafTokens(SCIENCE_ENTITY_DEFLECTOR_SHIELDS,                 0);
    setPureLeafTokens(SCIENCE_ENTITY_WEAPON_ENERGY_CIRCUIT,             0);
    setPureLeafTokens(SCIENCE_ENTITY_DRONE_MISSILE_LAUNCHER,            0);
    setPureLeafTokens(SCIENCE_ENTITY_DRONE_MISSILES,                    0);
    setPureLeafTokens(SCIENCE_ENTITY_LASER_GUN,                         0);
    setPureLeafTokens(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR,          0);
    setPureLeafTokens(SCIENCE_ENTITY_MAGNETICAL_COLLECTORS_OF_PLASMA,   0);
    setPureLeafTokens(SCIENCE_ENTITY_SUBMERSIBLE_FILTRATION_TANK,       0);
    setPureLeafTokens(SCIENCE_ENTITY_SUBMARINE_MINING_MACHINE,          0);
    setPureLeafTokens(SCIENCE_ENTITY_UNDERGROUND_DRILLING_MACHINE,      0);
    setPureLeafTokens(SCIENCE_ENTITY_PROBE,                             0);
    setPureLeafTokens(SCIENCE_ENTITY_HIGH_RESOLUTION_SENSORS,           0);
    setPureLeafTokens(SCIENCE_ENTITY_LONG_RANGE_SENSORS,                0);
    setPureLeafTokens(SCIENCE_ENTITY_CLOAKING_DEVICE,                   0);
    setPureLeafTokens(SCIENCE_ENTITY_ANDROIDS,                          0);
    setPureLeafTokens(SCIENCE_ENTITY_SCIENTIFIC_LAB,                    0);
    setPureLeafTokens(SCIENCE_ENTITY_LIFE_SUPPORT,                      0);
    setPureLeafTokens(SCIENCE_ENTITY_MAINFRAME,                         0);
    setPureLeafTokens(SCIENCE_ENTITY_HANGAR,                            0);
    setPureLeafTokens(SCIENCE_ENTITY_MAGNETICAL_FILTER,                 0);
    setPureLeafTokens(SCIENCE_ENTITY_DEUTERIUM_DECOMPOSER,              0);
    setPureLeafTokens(SCIENCE_ENTITY_ZIRCONIUM_REACTOR,                 0);
    setPureLeafTokens(SCIENCE_ENTITY_ILMENITE_FURNACE,                  0);

    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_SCIENCE,                  0);

    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_DRIVE,                    0);
    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_PLATFORM,                 0);
    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_MODULES,                  0);
    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_PRODUCTION,               0);

    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_SUBSPACE_DRIVE,           0);
    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_IMPULS_DRIVE,             0);
    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_CONSTRUCTION,             0);
    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_EQUIPMENT,                0);
    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_ENERGETIC_RING,           0);
    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_RECOGNITION_DEVICES,      0);
    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_FACTORIES,                0);
    setPureInnerTokens(SCIENCE_SUPREME_ENTITY_QUARRYING_MACHINES,       0);


    setSpecLeafTokens(SCIENCE_ENTITY_QUARK_GLUON_CORE,                  INDEX_QUARK_GLUON_CORE_SPEED,                           0);
    setSpecLeafTokens(SCIENCE_ENTITY_QUARK_GLUON_CORE,                  INDEX_QUARK_GLUON_CORE_CONSUMPTION,                     0);

    setSpecLeafTokens(SCIENCE_ENTITY_NAVIGATION_COMPUTER,               INDEX_NAVIGATION_COMPUTER_SPEED,                        0);

    setSpecLeafTokens(SCIENCE_ENTITY_SUBSPACE_BEACON,                   INDEX_SUBSPACE_BEACON_SPEED,                            0);

    setSpecLeafTokens(SCIENCE_ENTITY_INERTIAL_DAMPERS,                  INDEX_INERTIAL_DAMPERS_SPEED,                           0);

    setSpecLeafTokens(SCIENCE_ENTITY_IMPULS_JET_ENGINE,                 INDEX_IMPULS_ENGINE_FORCE,                              0);
    setSpecLeafTokens(SCIENCE_ENTITY_IMPULS_JET_ENGINE,                 INDEX_IMPULS_ENGINE_CONSUMPTION,                        0);

    setSpecLeafTokens(SCIENCE_ENTITY_MECHANICAL_DAMPERS,                INDEX_MECHANICAL_DAMPERS_ACCELERATION,                  0);

    setSpecLeafTokens(SCIENCE_ENTITY_DRONE_MISSILES,                    INDEX_DRONE_MISSILES_DAMAGE,                            0);
    setSpecLeafTokens(SCIENCE_ENTITY_DRONE_MISSILES,                    INDEX_DRONE_MISSILES_PRECISION,                         0);
    setSpecLeafTokens(SCIENCE_ENTITY_DRONE_MISSILES,                    INDEX_DRONE_MISSILES_RANGE,                             0);

    setSpecLeafTokens(SCIENCE_ENTITY_DRONE_MISSILE_LAUNCHER,            INDEX_DRONE_MISSILE_LAUNCHER_ACCUMULATOR,               0);
    setSpecLeafTokens(SCIENCE_ENTITY_DRONE_MISSILE_LAUNCHER,            INDEX_DRONE_MISSILE_LAUNCHER_CHARGING,                  0);

    setSpecLeafTokens(SCIENCE_ENTITY_SHIPS,                             INDEX_SHIPS_LEVEL,                                      0);

    setSpecLeafTokens(SCIENCE_ENTITY_BASES,                             INDEX_BASES_LEVEL,                                      0);

    setSpecLeafTokens(SCIENCE_ENTITY_HANGAR,                            INDEX_HANGARS_LEVEL,                                    0);

    setSpecLeafTokens(SCIENCE_ENTITY_ANDROIDS,                          INDEX_ANDROIDS_LEVEL,                                   0);
    setSpecLeafTokens(SCIENCE_ENTITY_ANDROIDS,                          INDEX_ANDROIDS_CONSUMPTION,                             0);

    setSpecLeafTokens(SCIENCE_ENTITY_ENERGETIC_ACCUMULATOR,             INDEX_ENERGETIC_ACCUMULATOR_ACCUMULATOR,                0);
    setSpecLeafTokens(SCIENCE_ENTITY_ENERGETIC_ACCUMULATOR,             INDEX_ENERGETIC_ACCUMULATOR_CHARGING,                   0);

    setSpecLeafTokens(SCIENCE_ENTITY_MAINFRAME,                         INDEX_MAINFRAME_BIO_NODES,                              0);
    setSpecLeafTokens(SCIENCE_ENTITY_MAINFRAME,                         INDEX_MAINFRAME_PREDICTION,                             0);
    setSpecLeafTokens(SCIENCE_ENTITY_MAINFRAME,                         INDEX_MAINFRAME_TIME_REUSABILITY,                       0);
    setSpecLeafTokens(SCIENCE_ENTITY_MAINFRAME,                         INDEX_MAINFRAME_CONSUMPTION,                            0);

    setSpecLeafTokens(SCIENCE_ENTITY_SCIENTIFIC_LAB,                    INDEX_SCIENTIFIC_LABS_LEVEL,                            0);

    setSpecLeafTokens(SCIENCE_ENTITY_LIFE_SUPPORT,                      INDEX_LIFE_SUPPORT_CAPACITY,                            0);
    setSpecLeafTokens(SCIENCE_ENTITY_LIFE_SUPPORT,                      INDEX_LIFE_SUPPORT_CONSUMPTION,                         0);

    setSpecLeafTokens(SCIENCE_ENTITY_WEAPON_ENERGY_CIRCUIT,             INDEX_WEAPON_ENERGY_CIRCUIT_CHARGING,                   0);

    setSpecLeafTokens(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR,          INDEX_TRANSPORT_BEAM_GENERATOR_ACCUMULATOR,             0);
    setSpecLeafTokens(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR,          INDEX_TRANSPORT_BEAM_GENERATOR_CHARGING,                0);
    setSpecLeafTokens(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR,          INDEX_TRANSPORT_BEAM_GENERATOR_FORCIBILITY,             0);
    setSpecLeafTokens(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR,          INDEX_TRANSPORT_BEAM_GENERATOR_PRECISION,               0);
    setSpecLeafTokens(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR,          INDEX_TRANSPORT_BEAM_GENERATOR_RANGE,                   0);

    setSpecLeafTokens(SCIENCE_ENTITY_LASER_GUN,                         INDEX_LASER_GUN_ACCUMULATOR,                            0);
    setSpecLeafTokens(SCIENCE_ENTITY_LASER_GUN,                         INDEX_LASER_GUN_CHARGING,                               0);
    setSpecLeafTokens(SCIENCE_ENTITY_LASER_GUN,                         INDEX_LASER_GUN_FORCIBILITY,                            0);
    setSpecLeafTokens(SCIENCE_ENTITY_LASER_GUN,                         INDEX_LASER_GUN_PRECISION,                              0);
    setSpecLeafTokens(SCIENCE_ENTITY_LASER_GUN,                         INDEX_LASER_GUN_RANGE,                                  0);

    setSpecLeafTokens(SCIENCE_ENTITY_DEFLECTOR_SHIELDS,                 INDEX_DEFLECTOR_SHIELDS_GENERATOR_SOLIDITY,             0);
    setSpecLeafTokens(SCIENCE_ENTITY_DEFLECTOR_SHIELDS,                 INDEX_DEFLECTOR_SHIELDS_GENERATOR_ACCUMULATOR,          0);
    setSpecLeafTokens(SCIENCE_ENTITY_DEFLECTOR_SHIELDS,                 INDEX_DEFLECTOR_SHIELDS_GENERATOR_CHARGING,             0);

    setSpecLeafTokens(SCIENCE_ENTITY_HIGH_RESOLUTION_SENSORS,           INDEX_HIGH_RESOLUTION_SENSORS_LEVEL,                    0);
    setSpecLeafTokens(SCIENCE_ENTITY_HIGH_RESOLUTION_SENSORS,           INDEX_HIGH_RESOLUTION_SENSORS_RANGE,                    0);
    setSpecLeafTokens(SCIENCE_ENTITY_HIGH_RESOLUTION_SENSORS,           INDEX_HIGH_RESOLUTION_SENSORS_CONSUMPTION,              0);

    setSpecLeafTokens(SCIENCE_ENTITY_LONG_RANGE_SENSORS,                INDEX_LONG_RANGE_SENSORS_LEVEL,                         0);
    setSpecLeafTokens(SCIENCE_ENTITY_LONG_RANGE_SENSORS,                INDEX_LONG_RANGE_SENSORS_RANGE,                         0);
    setSpecLeafTokens(SCIENCE_ENTITY_LONG_RANGE_SENSORS,                INDEX_LONG_RANGE_SENSORS_CONSUMPTION,                   0);

    setSpecLeafTokens(SCIENCE_ENTITY_CLOAKING_DEVICE,                   INDEX_CLOAKING_DEVICE_LEVEL,                            0);
    setSpecLeafTokens(SCIENCE_ENTITY_CLOAKING_DEVICE,                   INDEX_CLOAKING_DEVICE_CONSUMPTION,                      0);

    setSpecLeafTokens(SCIENCE_ENTITY_PROBE,                             INDEX_PROBE_DEVICE_LEVEL,                               0);

    setSpecLeafTokens(SCIENCE_ENTITY_MAGNETICAL_FILTER,                 INDEX_MAGNETICAL_FILTER_PRODUCTION,                     0);

    setSpecLeafTokens(SCIENCE_ENTITY_DEUTERIUM_DECOMPOSER,              INDEX_DEUTERIUM_DECOMPOSER_PRODUCTION,                  0);
    setSpecLeafTokens(SCIENCE_ENTITY_DEUTERIUM_DECOMPOSER,              INDEX_DEUTERIUM_DECOMPOSER_METAMORPHOSIS,               0);

    setSpecLeafTokens(SCIENCE_ENTITY_ZIRCONIUM_REACTOR,                 INDEX_ZIRCONIUM_REACTOR_PRODUCTION,                     0);
    setSpecLeafTokens(SCIENCE_ENTITY_ZIRCONIUM_REACTOR,                 INDEX_ZIRCONIUM_REACTOR_METAMORPHOSIS,                  0);

    setSpecLeafTokens(SCIENCE_ENTITY_ILMENITE_FURNACE,                  INDEX_ILMENITE_FURNACE_PRODUCTION,                      0);
    setSpecLeafTokens(SCIENCE_ENTITY_ILMENITE_FURNACE,                  INDEX_ILMENITE_FURNACE_METAMORPHOSIS,                   0);

    setSpecLeafTokens(SCIENCE_ENTITY_MAGNETICAL_COLLECTORS_OF_PLASMA,   INDEX_MAGNETICAL_COLLECTORS_OF_PLASMA_WINDING_SPEED,    0);

    setSpecLeafTokens(SCIENCE_ENTITY_SUBMERSIBLE_FILTRATION_TANK,       INDEX_SUBMERSIBLE_FILTRATION_TANK_WINDING_SPEED,        0);
    setSpecLeafTokens(SCIENCE_ENTITY_SUBMERSIBLE_FILTRATION_TANK,       INDEX_SUBMERSIBLE_FILTRATION_TANK_RANGE,                0);

    setSpecLeafTokens(SCIENCE_ENTITY_SUBMARINE_MINING_MACHINE,          INDEX_SUBMARINE_MINING_MACHINE_WINDING_SPEED,           0);
    setSpecLeafTokens(SCIENCE_ENTITY_SUBMARINE_MINING_MACHINE,          INDEX_SUBMARINE_MINING_MACHINE_RANGE,                   0);

    setSpecLeafTokens(SCIENCE_ENTITY_UNDERGROUND_DRILLING_MACHINE,      INDEX_UNDERGROUND_DRILLING_MACHINE_WINDING_SPEED,       0);
    setSpecLeafTokens(SCIENCE_ENTITY_UNDERGROUND_DRILLING_MACHINE,      INDEX_UNDERGROUND_DRILLING_MACHINE_RANGE,               0);

    return 0;
}

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

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