#pragma once

class Sprite
{
public:
	struct size
	{
		float x = 1;
		float y = 1;
	};

	struct size sizeSprite;
	struct size sizeTextureCoords;
	struct size shift;
	glm::vec3 position;
	//struct size position;

	GLuint shader;
	GLuint meshSprite = NULL;
	GLuint texture = 0;
	const GLchar* addImage;
	glm::mat4 spriteLocations;

	GLuint createShaders(const GLchar* vertexPath, const GLchar* fragmentPath)
	{
		unsigned int Program;
		const GLchar* vShaderCode = vertexPath;
		const GLchar * fShaderCode = fragmentPath;
		GLuint vertex, fragment;
		GLint success;
		GLchar infoLog[512];
		vertex = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vertex, 1, &vShaderCode, NULL);
		glCompileShader(vertex);
		glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
		if (!success)
		{
			glGetShaderInfoLog(vertex, 512, NULL, infoLog);
			std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
		}
		fragment = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fragment, 1, &fShaderCode, NULL);
		glCompileShader(fragment);
		glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
		if (!success)
		{
			glGetShaderInfoLog(fragment, 512, NULL, infoLog);
			std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
		}
		Program = glCreateProgram();
		glAttachShader(Program, vertex);
		glAttachShader(Program, fragment);
		glLinkProgram(Program);
		glGetProgramiv(Program, GL_LINK_STATUS, &success);
		if (!success)
		{
			glGetProgramInfoLog(Program, 512, NULL, infoLog);
			std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
		}
		glDeleteShader(vertex);
		glDeleteShader(fragment);
		return Program;
	}

	GLuint createPlane()
	{
		GLfloat verticesSource[] = {
			// Positions                                        // Texture Coords
			 0.5f * sizeSprite.x,  0.5f * sizeSprite.y, 0.0f,   -1.0f * sizeTextureCoords.x + shift.x, -1.0f * sizeTextureCoords.y + shift.y,
			 0.5f * sizeSprite.x, -0.5f * sizeSprite.y, 0.0f,   -1.0f * sizeTextureCoords.x + shift.x, 0.0f * sizeTextureCoords.y + shift.y,
			-0.5f * sizeSprite.x,  0.5f * sizeSprite.y, 0.0f,   0.0f * sizeTextureCoords.x + shift.x, -1.0f * sizeTextureCoords.y + shift.y,

			 0.5f * sizeSprite.x, -0.5f * sizeSprite.y, 0.0f,   -1.0f * sizeTextureCoords.x + shift.x, 0.0f * sizeTextureCoords.y + shift.y,
			-0.5f * sizeSprite.x, -0.5f * sizeSprite.y, 0.0f,   0.0f * sizeTextureCoords.x + shift.x, 0.0f * sizeTextureCoords.y + shift.y,
			-0.5f * sizeSprite.x,  0.5f * sizeSprite.y, 0.0f,   0.0f * sizeTextureCoords.x + shift.x, -1.0f * sizeTextureCoords.y + shift.y
		};

		GLuint VBO, VAO;
		glGenVertexArrays(1, &VAO);
		glGenBuffers(1, &VBO);

		glBindBuffer(GL_ARRAY_BUFFER, VBO);
		glBufferData(GL_ARRAY_BUFFER, sizeof(verticesSource), verticesSource, GL_STATIC_DRAW);

		glBindVertexArray(VAO);
		// Position attribute
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
		glEnableVertexAttribArray(0);
		// Normal attribute
		glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
		glEnableVertexAttribArray(1);
		glBindVertexArray(0);

		// очищаем сатрый vao
		glDeleteVertexArrays(1, &meshSprite);
		return VAO;
	}

	GLuint createTexture()
	{
		// создание текстуры
		GLuint texture;
		glGenTextures(1, &texture);
		glBindTexture(GL_TEXTURE_2D, texture);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		// Set texture filtering parameters
		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_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		// Load image, create texture and generate mipmaps
		int width, height;
		unsigned char* image = SOIL_load_image(addImage, &width, &height, 0, SOIL_LOAD_RGBA);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
		glGenerateMipmap(GL_TEXTURE_2D);
		SOIL_free_image_data(image);
		glBindTexture(GL_TEXTURE_2D, 0);
		return texture;
	}

	void simpleShader()
	{
		const GLchar* vertexShaderSource = "#version 330 core\n"
			"layout(location = 0) in vec3 position;\n"
			"layout(location = 1) in vec2 texCoord;\n"
			"out vec2 TexCoord;\n"
			"uniform mat4 location;\n"
			"uniform mat4 view;\n"
			"uniform mat4 projection;\n"
			"void main()\n"
			"{\n"
			"gl_Position = projection * view *  location * vec4(position, 1.0f);\n"
			"TexCoord = texCoord;\n"
			"}\n\0";

		const GLchar* fragmentShaderSource = "#version 330 core\n"
			"in vec2 TexCoord;\n"
			"out vec4 color;\n"
			"uniform sampler2D ourTexture;\n"
			"void main()\n"
			"{\n"
			"color = texture(ourTexture, TexCoord);\n"
			"}\n\0";

		shader = createShaders(vertexShaderSource, fragmentShaderSource);

		meshSprite = createPlane();

		texture = createTexture();
	}

	Sprite(const GLchar* addressImage, float x, float y, int typeShader)
	{
		addImage = addressImage;
		sizeSprite.x = x;
		sizeSprite.y = y;

		switch (typeShader)
		{
		case 1:
			simpleShader();
			break;
		default:
			simpleShader();
			break;
		}
	}

	void translatePosition(glm::vec3 new_position)
	{
		spriteLocations = glm::translate(spriteLocations, new_position);
		position = new_position;
	}

	glm::vec3 objectPosition()
	{
		return glm::vec3(glm::value_ptr(spriteLocations)[12], glm::value_ptr(spriteLocations)[13], glm::value_ptr(spriteLocations)[14]);
	}
};

bool checkCollision(Sprite *objectOne, Sprite *objectTwo)
{
	float distancePointsWidth = abs(glm::value_ptr(objectOne->spriteLocations)[12] - glm::value_ptr(objectTwo->spriteLocations)[12]);
	float distancePointsHeight = abs(glm::value_ptr(objectOne->spriteLocations)[13] - glm::value_ptr(objectTwo->spriteLocations)[13]);
	if (((objectOne->sizeSprite.x / 2) + (objectTwo->sizeSprite.x / 2)) > distancePointsWidth && ((objectOne->sizeSprite.y / 2) + (objectTwo->sizeSprite.y / 2)) > distancePointsHeight)
	{
		return true;
	}
	return false;
}



