

#include "GameLogic.h"

#include "glm.hpp"
#include "gtc/matrix_transform.hpp"
#include "InputManager.h"
#include "Geometry.h"
#include "Quad.h"

#include <random>
#include <algorithm>
#include <chrono>
#include <iostream>

#include "PerlinNoise.hpp"

glm::mat4 GameLogic::viewMatrix = glm::mat4(1.0f);
glm::mat4 GameLogic::projMatrix = glm::ortho(-2.0f, 2.0f, -1.0f, 1.0f, 0.1f, 100.0f);

GLuint GameLogic::tex = 0;

std::vector<double> GameLogic::randomNumbers(PIX_X * PIX_Y);

void GameLogic::init()
{

	loadRandomNumbers(randomNumbers);

	std::vector<std::array<float, 3>> pixels(PIX_X * PIX_Y);
	loadTexturePixels(pixels);

	glGenTextures(1, &tex);
	fillTexture(tex, pixels);

}


void GameLogic::update()
{
}

void GameLogic::render()
{
	glClearColor(32 / 255.0f, 178 / 355.0f, 170 / 255.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);
	
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, tex);

	static Shader shader = Shader("../assets/textShader.sh");
	static float texPosDim[4] = { 0, 0, 1, 1 };
	shader.setUniform4f("u_texPosDim", texPosDim);
	shader.setUniform1i("u_texture", 0);
	static Quad q;
	q.setDim(2, 2);
	q.setPos(-1, -1);

	q.render(&shader);

	glBindTexture(GL_TEXTURE_2D, 0);

}

void GameLogic::cleanup()
{
}


void GameLogic::keyPressed(int key, int action, int mods)
{
	if (key == GLFW_KEY_N && action == GLFW_PRESS) {

		loadRandomNumbers(randomNumbers);

		std::vector<std::array<float, 3>> pixels(PIX_X * PIX_Y);
		loadTexturePixels(pixels);

		fillTexture(tex, pixels);

	}
}


void GameLogic::loadTexturePixels(std::vector<std::array<float, 3>>& pixels)
{
	for (int x = 0; x < PIX_X; x++) {
		for (int y = 0; y < PIX_Y; y++) {

			int smoothNess = 30;
			int gap = 1;

			float noiseLevel = 0; 
			float xf = (float)x, yf = (float)y;
			for (int i = 0; i < smoothNess; i++) {
				for (int j = 0; j < smoothNess; j++) {
					noiseLevel += perlinNoise(xf + i * gap, yf + j * gap);
				}
			}
			noiseLevel /= (smoothNess * smoothNess);

			float contrast = 1.5f;
			float brightness = 0.0f;
			//noiseLevel = contrast * (noiseLevel - 0.5f) + 0.5f + brightness;

			int topoLevels = 20;
			noiseLevel = (int)(noiseLevel * topoLevels) / (float)topoLevels;

			float waterLevel = 0.5f;
			if (noiseLevel > waterLevel) {
				// land
				noiseLevel -= waterLevel;
				noiseLevel /= 1.0f - waterLevel;

				float landContrast = 1.0f;
				float landBrightness = 0.0f;
				noiseLevel = landContrast * (noiseLevel - 0.5f) + 0.5f + landBrightness;
				pixels[x + y * PIX_X] = { noiseLevel / 10, noiseLevel, noiseLevel / 10 };
			}
			else {
				// water
				noiseLevel /= waterLevel;

				float waterContrast = 1.0f;
				float waterBrightness = 0.0f;
				noiseLevel = waterContrast * (noiseLevel - 0.5f) + 0.5f + waterBrightness;
				pixels[x + y * PIX_X] = { noiseLevel / 10, noiseLevel / 10, noiseLevel };
			}
		}
	}
}


void GameLogic::fillTexture(GLuint tex, std::vector<std::array<float, 3>>& pixels)
{
	glBindTexture(GL_TEXTURE_2D, tex);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	loadRandomNumbers(randomNumbers);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, PIX_X, PIX_Y, 0, GL_RGB, GL_FLOAT, &pixels[0]);

	glBindTexture(GL_TEXTURE_2D, 0);
}


void GameLogic::loadRandomNumbers(std::vector<double>& numbers)
{
	srand(time(NULL));

	std::random_device generator;
	siv::PerlinNoise perlinA(generator);
	perlinA.reseed(time(NULL));

	double octaves;
	double frequency;

	int x, y;
	for (int i = 0; i < numbers.size(); i++) {
		x = i % PIX_X;
		y = i / PIX_Y;

		octaves = 10;
		frequency = 800;
		numbers[i] = (float)(perlinA.accumulatedOctaveNoise2D_0_1((double)x / frequency, (double)y / frequency, octaves));

	}

}


double GameLogic::perlinNoise(double x, double y)
{
	int index = x + y * PIX_X;
	if (index < 0) index = 0;
	if (index > randomNumbers.size()) index = 0;
	return randomNumbers[index];
}