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

#include "common_routines.h"

#include <cstdlib>
#include <math.h>
#include <time.h>

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

bool rollADice(double chance)
{
	return (rand() % 2000) < (chance * 2000);
};

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

bool rollAWellBalancedDice(double chance)
{
	return (rand() % RAND_MAX) < (chance * RAND_MAX);
};

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

unsigned long rollANSidedDice(unsigned long N)
{
	if (0 < N)
	{
		return ((rand() % N) + 1);
	}
	else
	{
		return 0;
	}
};

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

unsigned long rollA0ToNSidedDice(unsigned long N)
{
	return (rand() % (N + 1));
};

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

unsigned short usRollA0ToNSidedDice(unsigned short N)
{
	return (rand() % (N + 1));
};

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

bool flipACoin()
{
	return rollADice(0.5f);
};

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

void ulShiftPile(unsigned long &a, unsigned long &b, unsigned long shiftFromAToB)
{
	unsigned long shift;
	shift  = min(a, shiftFromAToB);
	a     -= shift;
	b     += shift;
};

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

void ullShiftPile(unsigned long long &a, unsigned long long &b, unsigned long long shiftFromAToB)
{
	unsigned long long shift;
	shift  = min(a, shiftFromAToB);
	a     -= shift;
	b     += shift;
};

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

void divideIntoPiles(unsigned long long count, stacksAndLimits piles)
{
	while ((0 < piles.size()) && (0 < count))
	{
		unsigned long long addingPart   = count / piles.size();
		addingPart                      = max((unsigned long long)1, addingPart);
		stacksAndLimits::iterator litp1 = piles.begin();
		while ((litp1 != piles.end()) && (0 < count))
		{
			unsigned long long realAdd  = min(addingPart, litp1->second - (*(litp1->first)));
			(*(litp1->first))          += realAdd;
			count                      -= realAdd;
			(*(litp1->first))           = min((*(litp1->first)), litp1->second);
			if ((*(litp1->first))      == litp1->second)
			{
				stacksAndLimits::iterator prev = litp1;
				litp1++;
				piles.erase(prev);
			}
			else
			{
				litp1++;
			}
		}
	}
};

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

unsigned long long ullCheckRange(unsigned long long bottom, unsigned long long top, unsigned long long value)
{
	if (top < bottom)
	{
		errLogicalError("invalid range - (%u, %u)", (unsigned long long)bottom, (unsigned long long)top);
		unsigned long long buff = bottom;
		bottom = top;
		top    = buff;
	}

	value = min(top   , value);
	value = max(bottom, value);

	return value;
};

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

unsigned long      ulCheckRange(unsigned long bottom, unsigned long top, unsigned long value)
{
	if (top < bottom)
	{
		errLogicalError("invalid range - (%u, %u)", (unsigned long long)bottom, (unsigned long long)top);
		unsigned long buff = bottom;
		bottom = top;
		top    = buff;
	}

	value = min(top   , value);
	value = max(bottom, value);

	return value;
};

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

unsigned long long ullHandleZero(unsigned long long number)
{
	if (0 < number)
	{
		return number;
	}
	else
	{
		errExceptionError("protection from zero division");
		return 1;
	}
}

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

unsigned long ulHandleZero(unsigned long number)
{
	if (0 < number)
	{
		return number;
	}
	else
	{
		errExceptionError("protection from zero division");
		return 1;
	}
}

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

double dHandleZero(double number)
{
	if (0 < number)
	{
		return number;
	}
	else
	{
		errExceptionError("protection from zero division");
		return (double)(1);
	}
}

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

unsigned long long ullRound (double number)
{
	return (unsigned long long) (floor(number + 0.5));
}

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

unsigned long ulRound (double number)
{
	return (unsigned long) (floor(number + 0.5));
}

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

unsigned long long ullUpRound (double number)
{
	return (unsigned long long) (ceil(number));
}

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

unsigned long ulUpRound (double number)
{
	return (unsigned long) (ceil(number));
}

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

unsigned short usUpRound (double number)
{
	return (unsigned short) (ceil(number));
}

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

unsigned long long ullSafeSub(unsigned long long a, unsigned long long b)
{
	return a - min(a, b);
}

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

unsigned long ulSafeSub(unsigned long a, unsigned long b)
{
	return a - min(a, b);
}

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

unsigned short usSafeSub(unsigned short a, unsigned short b)
{
	return a - min(a, b);
}

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

double dSafeSub(double a, double b)
{
	if (a < b)
	{
		return 0;
	}
	else
	{
		return a - b;
	}
}

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

unsigned long long ullUpRoundDiv(unsigned long long dividend, unsigned long long divider)
{
	unsigned long long result = dividend / ullHandleZero(divider);

	if (dividend > (divider * result))
	{
		++result;
	}

	return result;
}

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

unsigned long ulUpRoundDiv(unsigned long dividend, unsigned long divider)
{
	unsigned long result = dividend / ulHandleZero(divider);

	if (dividend > (divider * result))
	{
		++result;
	}

	return result;
}

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

unsigned long long ullNumberFromScale(unsigned long long bottom, unsigned long long top, double scaleRange, double pointOnScale)
{
	if (0 < scaleRange)
	{
		return (unsigned long long)(bottom + ((top - bottom) * (pointOnScale / dHandleZero(scaleRange))));
	}
	else
	{
		return top;
	}
}

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

unsigned long ulNumberFromScale(unsigned long bottom, unsigned long top, double scaleRange, double pointOnScale)
{
	if (0 < scaleRange)
	{
		return (unsigned long)(bottom + ((top - bottom) * (pointOnScale / dHandleZero(scaleRange))));
	}
	else
	{
		return top;
	}
}

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

long lNumberFromScale(long bottom, long top, double scaleRange, double pointOnScale)
{
	if (0 < scaleRange)
	{
		return (long)(bottom + ((top - bottom) * (pointOnScale / dHandleZero(scaleRange))));
	}
	else
	{
		return top;
	}
}

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

double dNumberFromScale(double bottom, double top, double scaleRange, double pointOnScale)
{
	if (0 < scaleRange)
	{
		return (bottom + ((top - bottom) * (pointOnScale / dHandleZero(scaleRange))));
	}
	else
	{
		return top;
	}
}

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

double dRandomNumberFromScale(double bottom, double top, unsigned long dicesCount)
{
	unsigned long long totalSum  = 0;
	unsigned long long randomSum = 0;
	for (unsigned long loop1 = 0; loop1 < dicesCount; loop1++)
	{
		totalSum  += RAND_MAX;
		randomSum += rand() % RAND_MAX;
	}
	return dNumberFromScale(bottom, top, (double)totalSum, (double)randomSum);
}

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

double dRandomNumberFromScale(double bottom, double top, unsigned long dicesCount, double exponentialChanceToGrow)
{
	unsigned long long maxSum    = (unsigned long long)dicesCount * (unsigned long long)RAND_MAX;
	unsigned long long totalSum  = 0;
	unsigned long long randomSum = 0;
	while ((totalSum <= maxSum                ) && 
		   (rollADice(exponentialChanceToGrow)))
	{
		totalSum  += RAND_MAX;
		randomSum += rand() % RAND_MAX;
	}

	if (bottom < top)
	{
		return dNumberFromScale(bottom, top, (double)maxSum, (double)randomSum);
	}
	else
	{
		return dNumberFromScale(top, bottom, (double)maxSum, (double)maxSum - (double)randomSum);
	}
}

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