// ################################
// ##       Obecne funkce        ##
// ##      herne nezavisle       ##
// ################################

//#define DEBUG_URZA
//#define CREATOR_URZA
#pragma once
#ifndef COMMON_ROUTINES_H
#define COMMON_ROUTINES_H

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

#include "common.h"

#include <list>

#include "error_handling.h"

using namespace std;

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

typedef pair<unsigned long      *, unsigned long     > unsignedLongPUnsignedLongPair;
typedef list<unsignedLongPUnsignedLongPair           > unsignedLongPUnsignedLongPairList;
typedef pair<unsigned long long *, unsigned long long> unsignedLongLongPUnsignedLongLongPair;
typedef list<unsignedLongLongPUnsignedLongLongPair   > unsignedLongLongPUnsignedLongLongPairList;

typedef unsignedLongPUnsignedLongPairList              oldStacksAndLimits;
typedef unsignedLongLongPUnsignedLongLongPairList      stacksAndLimits;

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

#ifdef DEBUG_URZA
#define DEBUG_URZA_FORTRESS 2
#include <fstream>
	extern ofstream debugOut;
#endif

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

class itemDimensionsClass
{
public:
	unsigned long long weight_; // hmotnost
	double             length_; // delka
	double             width_ ; // sirka
	double             height_; // vyska

	itemDimensionsClass(                                                                     ) { weight_ = 0     ; length_ = 0     ; width_ = 0    ; height_ = 0     ; };
	itemDimensionsClass(unsigned long long weight, double length, double width, double height) { weight_ = weight; length_ = length; width_ = width; height_ = height; };
	itemDimensionsClass(unsigned long long weight                                            ) { weight_ = weight; length_ = 0     ; width_ = 0    ; height_ = 0     ; };
	itemDimensionsClass(                           double length, double width, double height) { weight_ = 0     ; length_ = length; width_ = width; height_ = height; };

	double getSize() { return length_ * width_ * height_; };
	double ratioForSomethingToFitInHere(double length, double width, double height) { return min(min(length_ / length, width_ / width), height_ / height); };
};

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

bool               rollADice(double chance);
bool               rollAWellBalancedDice(double chance);
unsigned long      rollANSidedDice(unsigned long N);
unsigned long      rollA0ToNSidedDice(unsigned long N);
unsigned short     usRollA0ToNSidedDice(unsigned short N);
bool               flipACoin();

void               ulShiftPile(unsigned long &a, unsigned long &b, unsigned long shiftFromAToB);
void               ullShiftPile(unsigned long long &a, unsigned long long &b, unsigned long long shiftFromAToB);
void               divideIntoPiles(unsigned long long count, stacksAndLimits piles);
unsigned long long ullCheckRange(unsigned long long bottom, unsigned long long top, unsigned long long value);
unsigned long      ulCheckRange(unsigned long bottom, unsigned long top, unsigned long value);

unsigned long long ullHandleZero(unsigned long long number);
unsigned long      ulHandleZero(unsigned long number);
double             dHandleZero(double number);

unsigned long long ullRound   (double number);
unsigned long      ulRound    (double number);
unsigned long long ullUpRound (double number);
unsigned long      ulUpRound  (double number);
unsigned short     usUpRound  (double number);

unsigned long long ullUpRoundDiv(unsigned long long dividend, unsigned long long divider);
unsigned long      ulUpRoundDiv(unsigned long dividend, unsigned long divider);
unsigned long long ullSafeSub(unsigned long long a, unsigned long long b);
unsigned long      ulSafeSub(unsigned long a, unsigned long b);
unsigned short     usSafeSub(unsigned short a, unsigned short b);
double             dSafeSub(double a, double b);

unsigned long long ullNumberFromScale(unsigned long long bottom, unsigned long long top, double scaleRange, double pointOnScale);
unsigned long      ulNumberFromScale(unsigned long bottom, unsigned long top, double scaleRange, double pointOnScale);
long               lNumberFromScale(long bottom, long top, double scaleRange, double pointOnScale);
double             dNumberFromScale(double bottom, double top, double scaleRange, double pointOnScale);
double             dRandomNumberFromScale(double bottom, double top, unsigned long dicesCount);
double             dRandomNumberFromScale(double bottom, double top, unsigned long dicesCount, double exponentialChanceToGrow);

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

template<class containerType, class itemsType>  
unsigned long eraseByValue(containerType & container, itemsType value, unsigned long maxErasedItems)
{	
	unsigned long originalMaxErasedItems = maxErasedItems;
	containerType::iterator litp1 = container.begin();
	
	while (litp1 != container.end())
	{
		if (0 == maxErasedItems)
			return originalMaxErasedItems - maxErasedItems;

		if (value == (*litp1))
		{
			litp1 = container.erase(litp1);
			maxErasedItems--;
		}
		else
		{
			litp1++;
		}
	}

	return originalMaxErasedItems - maxErasedItems;
}

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

template<class containerType, class itemsType>  
unsigned long eraseByFirstValueInPair(containerType & container, itemsType value, unsigned long maxErasedItems)
{	
	unsigned long originalMaxErasedItems = maxErasedItems;
	containerType::iterator litp1 = container.begin();
	
	while (litp1 != container.end())
	{
		if (0 == maxErasedItems)
			return originalMaxErasedItems - maxErasedItems;

		if (value == (*litp1).first)
		{
			litp1 = container.erase(litp1);
			maxErasedItems--;
		}
		else
		{
			litp1++;
		}
	}

	return originalMaxErasedItems - maxErasedItems;
}

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

template<class containerType, class itemsType> 
bool isItemInContainer(containerType container, itemsType value)
{
	for (containerType::iterator litp1 = container.begin(); litp1 != container.end(); litp1++)	
	{
		if (value == (*litp1))
		{
			return 1;
		}
	}

	return 0;
}

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

template<class containerType, class itemsType>
void fillContainerWithValue(containerType & container, itemsType value)
{
	for (containerType::iterator litp1 = container.begin(); litp1 != container.end(); litp1++)
	{
		(*litp1) = value;
	}
}

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

template<class containerType, class itemsType> 
void addValueToWholeContainer (containerType & container, itemsType value)
{
	for (containerType::iterator litp1 = container.begin(); litp1 != container.end(); litp1++)
	{
		(*litp1) += value;
	}
}

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

template<class containerType, class itemsType>
itemsType sumWholeContainer (containerType & container)
{
	itemsType result = 0;
	for (containerType::iterator litp1 = container.begin(); litp1 != container.end(); litp1++)
	{
		result += (*litp1);
	}
	return result;
}

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

#endif