#include "FractalTerrainNode.h"

FractalTerrainNode::FractalTerrainNode(wstring name, wstring seed, float chunkSize, int widthX, int widthZ, float waterHeight, int cellSizeX, int cellSizeZ) : PerlinTerrainNode(name, seed, chunkSize, widthX, widthZ, waterHeight, cellSizeX, cellSizeZ)
{
	if (!_initError)
	{
		float p = 1.0f;

		for (int k = 1023; k > 2; k = (k + 1) / 2)
		{
			GenerateDiamonds(k, p);
			GenerateSquares(k, p);

			p *= 0.5f;
		}
	}
}

void FractalTerrainNode::GenerateSquares(const int k, const int p)
{
	int stride = k - 1;

	uniform_real_distribution<float> distro(0.0f, 1.0f);

	for (int z = k / 2; z < _gridRows; z += stride)
	{
		for (int x = 0; x < _gridCols; x += stride)
		{
			DoSquare(z, x, k, p * distro(_generator));
		}
	}

	for (int z = 0; z < _gridRows; z += stride)
	{
		for (int x = k / 2; x < _gridCols; x += stride)
		{
			DoSquare(z, x, k, p * distro(_generator));
		}
	}
}

void FractalTerrainNode::GenerateDiamonds(const int k, const int p)
{
	int stride = k - 1;

	uniform_real_distribution<float> distro(0.0f, 1.0f);

	for (int z = k * 0.5f; z < _gridRows; z += stride)
	{
		for (int x = k * 0.5f; x < _gridCols; x += stride)
		{
			DoDiamond(z, x, k, p * distro(_generator));
		}
	}
}

void FractalTerrainNode::DoSquare(const int row, const int col, const int k, const float offset)
{
	int step = k * 0.5f;
	vector<float> featurePoints;
	vector<vector<int>> points = { {row, col - step}, {row, col + step}, {row - step, col}, {row + step, col} };

	for (vector<int> point : points)
	{
		if (point[0] >= 0 && point[0] < (int)_gridRows && point[1] >= 0 && point[1] < (int)_gridCols)
		{
			int pointIndex = ((point[0] * _gridCols) + point[1]);
			featurePoints.push_back(_heightValues[pointIndex]);
		}
	}

	float newHeight = (accumulate(featurePoints.begin(), featurePoints.end(), 0.0f) / featurePoints.size()) + offset;
	int currentIndex = ((row * _gridCols) + col);
	_heightValues[currentIndex] = newHeight;
}

void FractalTerrainNode::DoDiamond(const int row, const int col, const int k, const float offset)
{
	int step = k * 0.5f;

	float newHeight = 0.0f;

	vector<vector<int>> points = { {row - step, col - step}, {row + step, col - step}, {row - step, col + step}, {row + step, col - step} };
	for (vector<int> point : points)
	{
		if (point[0] >= 0 && point[0] < (int)_gridRows && point[1] >= 0 && point[1] < (int)_gridCols)
		{
			int pointIndex = ((point[0] * _gridCols) + point[1]);
			newHeight += _heightValues[pointIndex];
		}
	}

	newHeight *= 0.25f;
	newHeight += offset;

	int currentIndex = ((row * _gridCols) + col);
	_heightValues[currentIndex] = newHeight;
}