#include "Game.h"
#include "Input.h"
#include <chrono>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <iostream>
#include "IGraphics.h"
#include "Graphics.h" // more of a generic system of graphics
#include "IInput.h"

Game::Game(const Input* const input, IGraphics* graphics) :
	input(input),
	graphics(graphics)

{
	printf("This cross project was partly inspired by BUas Student Ferri de Lange\n");
	printf("This GPU supplied by  :%s\n", glGetString(GL_VENDOR));
	printf("This GPU supports GL  :%s\n", glGetString(GL_VERSION));
	printf("This GPU Renders with :%s\n", glGetString(GL_RENDERER));
	printf("This GPU Shaders are  :%s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));

}

Game::~Game()
{

}
int DrawTriangle()
{
	Graphics GraphicFunctions;
	static GLuint PO = -1;
	if (PO == -1) // only do it once
	{ // take care to use the \n to end the line
		GLbyte vShaderStr[] =
			"precision highp float;\n" // we use highp since PC's dont' like using medium or low
			"attribute vec3 a_position;\n"
			"void main()\n"
			"{ \n"
			" gl_Position=vec4(a_position,1.0);	\n"
			"}\n";
		GLbyte fShaderStr[] =
			"precision highp float;\n"
			"void main()\n"
			"{\n"
			"gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
			"}\n";
		// Load and compile the vertex/fragment shaders
		GLuint vShader =
			GraphicFunctions.LoadShader(GL_VERTEX_SHADER, (const char*)vShaderStr);
		GLuint fShader =
			GraphicFunctions.LoadShader(GL_FRAGMENT_SHADER, (const char*)fShaderStr);

		// Create the program object	
		PO = glCreateProgram(); // save the PO and remvoe the -1 init value
		// now we have the V and F shaders  attach them to the progam object
		glAttachShader(PO, vShader);
		glAttachShader(PO, fShader);
		// Link the program
		glLinkProgram(PO);
		// Check the link status
		GLint AreTheylinked;
		glGetProgramiv(PO, GL_LINK_STATUS, &AreTheylinked);
		if (!AreTheylinked)
		{//they should link fine but lets report if there was an error
			GLint RetinfoLen = 0;
			// check and report any errors
			glGetProgramiv(PO, GL_INFO_LOG_LENGTH, &RetinfoLen);
			if (RetinfoLen > 1)
			{
				GLchar* infoLog = (GLchar*)malloc(sizeof(char) * RetinfoLen);
				glGetProgramInfoLog(PO, RetinfoLen, NULL, infoLog);
				fprintf(stderr, "Error linking program:\n%s\n", infoLog);
				free(infoLog);
			}
			glDeleteProgram(PO);
			return FALSE; //something bad happened
		}
	}
	//get ready to draw
	GLfloat TriVertices[] =
	{
		0.0f ,  0.5f, 0.0f,
		-0.5f,	-0.5f, 0.0f,
		0.5f , -0.5f, 0.0f
	};
	glUseProgram(PO);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, TriVertices);
	glEnableVertexAttribArray(0);
	glDrawArrays(GL_TRIANGLES, 0, 3);
	if (glGetError() != GL_NO_ERROR)
	{
		printf("Oh bugger"); // we use this so we can lock a breakpoint if needed
		return FALSE;
	}
	return TRUE;
}

 


void Game::Start()
{
	InitializeOpenGLES();

	// Timing
	auto startTime = std::chrono::system_clock::now();
	auto lastTime = startTime;

	float averageFPS{ 0 };
	glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

	while(!quitting)
	{
	
		ProcessInput();
		auto time = std::chrono::system_clock::now();
		std::chrono::duration<float> delta = time - lastTime;

		gameDeltaTime = delta.count();

		std::chrono::duration<float> elapsed = time - startTime;
		if(elapsed.count() > 0.25f && frameCount > 10)
		{
			averageFPS = static_cast<float>(frameCount) / elapsed.count();
			startTime = time;
			frameCount = 0;
		}
		ClearScreen();
		DrawTriangle();

		graphics->SwapBuffer();
		lastTime = time;
		++frameCount;
	}

	graphics->Quit();
}



const Input& Game::GetInput() const
{
	return *input;
}

void Game::Quit()
{
	quitting = true;
}


//example of using the key and mouse
void Game::ProcessInput()
{
	const Input& input = GetInput();
	const IMouse& mouse = GetInput().GetMouse();
	{
		if (input.GetKeyboard().GetKey(Key::W))
		{
			printf("we pressed W\n");
		}
		if (input.GetKeyboard().GetKey(Key::S))
		{
			printf("we pressed S\n");
		}
		if (input.GetKeyboard().GetKey(Key::A))
		{
			printf("we pressed A\n");
		}
		if (input.GetKeyboard().GetKey(Key::D))
		{
			printf("we pressed D\n");
		}

		if (input.GetKeyboard().GetKey(Key::ESCAPE))
		{
			printf("we pressed EscD\n");
			quitting = true;
		}

		if (input.GetMouse().GetButtonDown(MouseButtons::LEFT))
		{
			printf("we pressed mouse left\n");
		}
		// feel free to add mouse left and centre
	

	}



}



void Game::InitializeOpenGLES()
{

	glEnable(GL_DEPTH_TEST);
	glDepthMask(GL_TRUE);
	glDepthFunc(GL_LEQUAL);
	glDepthRangef(0.0f, 1.0f);
	glClearDepthf(1.0f);

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glBlendEquation(GL_FUNC_ADD);

	glCullFace(GL_BACK);
	glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
}

void Game::ClearScreen()
{
	glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
