#ifndef SVK_LOADER_HPP
#define SVK_LOADER_HPP

#include "svk_types.hpp"
#include "LittleOBJLoader.h"


bool _loadShaderModule(char const* filePath,
					   VkDevice device,
					   VkShaderModule& outShaderModule);

void _loadShaders(VkDevice device,
				  std::string const& vertPath,
				  std::string const& fragPath,
				  VkShaderModule& vertModule,
				  VkShaderModule& fragModule,
				  bool debug = false);

bool _isSourceUpdated(std::string const& sourcePath, std::string const& compiledPath, bool debug);
void _compileShader(std::string const& sourcePath, std::string const& compiledPath, bool debug);

void setShaderCompilerPath(std::string const& path);

bool loadTGATextureData(char const* filename, TextureData *texture);
void freeTextureData(TextureData* textureData);


template <VertexType type>
void _loadModel(SVKModel<type>& model, char const* filePath)
{
	ModelData* data = LoadModelData(filePath);

	std::unordered_map<Vertex<type>, uint32_t> uniqueVertices{};

	for (int i = 0; i < data->numIndices; ++i){
		unsigned index = data->indexArray[i];
		Vertex<type> vertex{};

		vertex.position = data->vertexArray[index];

		if (type & VERTEX_PN)
			vertex.normal = data->normalArray[index];

		if (type & VERTEX_PT)
			vertex.texCoord = vec2(
				data->texCoordArray[index].x,
				1.0f - data->texCoordArray[index].y);

		if (uniqueVertices.count(vertex) == 0){
			uniqueVertices[vertex] = static_cast<uint32_t>(model.vertices.size());
			model.vertices.push_back(vertex);
		}
		model.indices.push_back(uniqueVertices[vertex]);
	}
	DisposeModelData(data);
}

// Parser

struct Binding
{
   int binding;
   char name[1024]; // or string name
   bool texture;
   bool active;
   AllocatedImage image;
   AllocatedBuffer buffer;
   int buffersize;
};
// location names and numbers for attributes?
struct Attribute
{
	int location;
	char name[1024]; // or string name
	int size;
};

void getShaderInfo(const char *fileName, int &pushConstantSize, std::vector<Attribute> &attributesList,
std::vector<Binding> &bindingsList, bool isFrag);

int svkGetAttributeLocation(std::vector<Attribute> attributes, const char *s);
int svkGetBinding(std::vector<Binding> bindings, const char *s);

int svkGetAttributeLocationIndex(std::vector<Attribute> attributes, const char *s);
int svkGetBindingIndex(std::vector<Binding> bindings, const char *s);


// Shader/pipeline interface

typedef struct
{
  VkPipeline pipeline;
  VkPipelineLayout pipelineLayout;
  VkDescriptorSetLayout descriptorSetLayout;
  
  std::vector<Attribute> attributesList;
  std::vector<Binding> bindingsList;
  
  bool depth = true;
  bool alpha = false;
  bool stencil = false;

} PipelineData;

// Smarter, based on parsing the shaders
void svkBuildPipeline(PipelineData &p, const char *vertexShader, const char *fragmentShader);

// Simple 2-to-1 calls, not really necessary.
void svkAllocateAndUploadVertexBuffer(AllocatedBuffer &buffer, float *bufferData, int bufferSize);
void svkAllocateAndUploadIndexBuffer(AllocatedBuffer &buffer, uint32_t *bufferData, int bufferSize);

// Bind for drawing
void svkBindPipelineData(VkCommandBuffer cmd, PipelineData &p);
void svkValidator(PipelineData &p);
void svkBindPipeline(VkCommandBuffer cmd, PipelineData &p); // vkCmdBindPipeline + svkBindPipelineData

// Printout for debugging
void printPipelineData(PipelineData p);

// Assign buffers to uniforms
void svkInstallUniform(PipelineData &p, const char *name, AllocatedBuffer buffer);
void svkInstallTexture(PipelineData &p, const char *name, AllocatedImage texture);
//void setUniform(PipelineData &p, const char *name, AllocatedBuffer buffer, int bufferSize);
//void setTexture(PipelineData &p, const char *name, AllocatedImage texture);

void svkBindVertexBuffer(PipelineData pld, VkCommandBuffer cmd, AllocatedBuffer &buffer, const char *name);


// LittleOBJLoader, load and draw.

typedef struct Model
{
  AllocatedBuffer vertexBuffer, normalBuffer, texcoordBuffer, indexBuffer;
  ModelData *modelData;
  bool hasTexture;
} Model;

Model *LoadModel(const char *filename);
Model *LoadDataToModel(
              vec3 *vertices,
              vec3 *normals,
              vec2 *texCoords,
              uint *indices,
              int numVert,
              int numIndices
              );

// DrawModel woth explicit locations
void DrawModel(Model *model, VkCommandBuffer cmd, int b0, int b1, int b2);
// Assumes locations 0 1 2. Simple but requires you to know the default.
void DrawModel(Model *model, VkCommandBuffer cmd);
// Look up in the pipeline info. Not fast but easy.
void DrawModel(Model *model, VkCommandBuffer cmd, PipelineData pld, const char *vertex, const char *normal, const char *texCoord);


#endif // SVK_LOADER_HPP
