#pragma once

#include <glm/glm.hpp>
#include <glm/gtx/quaternion.hpp>
#include <iostream>

#include "mesh.h"

namespace kek {

template <typename... Args>
using GenericFunction = void (*)(Args... args);

template <typename... Args>
struct GenericCallable {
	GenericFunction<Args..., void *> function;
	void *data;

	GenericCallable()
		: function(nullptr),
		  data(nullptr) {}

	GenericCallable(GenericFunction<Args...> function)
		: GenericCallable([](Args... args, void *f) { ((GenericFunction<Args...>) f)(args...); }, (void *) function) {}

	GenericCallable(GenericFunction<Args..., void *> function, void *data)
		: function(function),
		  data(data) {}

	void operator()(Args... args) {
		if(function == nullptr) return;
		function(args..., data);
	}
};

typedef GenericCallable<> Callable;

float clampCyclic(float value, float min, float max);

float clamp(float value, float min, float max);

int clampCyclic(int value, int min, int max);

int clamp(int value, int min, int max);

template <typename T>
T clamp(T value, T min, T max) {
	if(value > max) value = max;
	if(value < min) value = min;
	return value;
}

template <typename T>
T clampCyclic(T value, T min, T max) {
	while(value > max) value -= (max - min);
	while(value < min) value += (max - min);
	return value;
}

enum class CubeFace {
	FRONT,
	BACK,
	UP,
	DOWN,
	LEFT,
	RIGHT
};

glm::vec3 faceNormal(CubeFace face);

void genCubeVertices(float w, float h, float d, bool texCoords, float **outVerts, size_t *outSize, size_t *vertexCount);

std::vector<Vertex> genCubeVertices(float w, float h, float d, bool texCoords);

Mesh *genCubeMesh(float w, float h, float d, std::shared_ptr<Texture> ambient, std::shared_ptr<Texture> diffuse, std::shared_ptr<Texture> specular);

std::string toString(glm::vec3 vector);

}