#include "ResourceManager.h"

#include <string>
#include <iostream>
#include <sstream>
#include <fstream>

// Instantiate static variables
std::map<std::string, Shader> ResourceManager::shaders;
std::map<std::string, LevelMap> ResourceManager::levelMaps;


Shader ResourceManager::LoadShader(const char* vShaderFile, const char* fShaderFile, const char* gShaderFile, std::string name)
{
    shaders[name] = LoadShaderFromFile(vShaderFile, fShaderFile, gShaderFile);
    return shaders[name];
}

Shader ResourceManager::GetShader(std::string name)
{
    return shaders[name];
}

void ResourceManager::Clear()
{
    // (properly) delete all shaders	
    for (auto iter : shaders)
        glDeleteProgram(iter.second.ID);
}

Shader ResourceManager::LoadShaderFromFile(const char* vShaderFile, const char* fShaderFile, const char* gShaderFile)
{
    // 1. retrieve the vertex/fragment source code from filePath
    std::string vertexCode;
    std::string fragmentCode;
    std::string geometryCode;
    try
    {
        // open files
        std::ifstream vertexShaderFile(vShaderFile);
        std::ifstream fragmentShaderFile(fShaderFile);
        std::stringstream vShaderStream, fShaderStream;
        // read file's buffer contents into streams
        vShaderStream << vertexShaderFile.rdbuf();
        fShaderStream << fragmentShaderFile.rdbuf();
        // close file handlers
        vertexShaderFile.close();
        fragmentShaderFile.close();
        // convert stream into string
        vertexCode = vShaderStream.str();
        fragmentCode = fShaderStream.str();
        // if geometry shader path is present, also load a geometry shader
        if (gShaderFile != nullptr)
        {
            std::ifstream geometryShaderFile(gShaderFile);
            std::stringstream gShaderStream;
            gShaderStream << geometryShaderFile.rdbuf();
            geometryShaderFile.close();
            geometryCode = gShaderStream.str();
        }
    }
    catch (std::exception e)
    {
        std::cout << "ERROR::SHADER: Failed to read shader files" << std::endl;
    }
    const char* vShaderCode = vertexCode.c_str();
    const char* fShaderCode = fragmentCode.c_str();
    const char* gShaderCode = geometryCode.c_str();
    // 2. now create shader object from source code
    Shader shader;
    shader.Compile(vShaderCode, fShaderCode, gShaderFile != nullptr ? gShaderCode : nullptr);
    return shader;
}

LevelMap ResourceManager::LoadLevelMap(const char* filePath, std::string name)
{
    levelMaps[name] = LoadLevelMapFromFile(filePath);
    return levelMaps[name];
}

LevelMap ResourceManager::LoadLevelMapFromFile(const char* filePath)
{
    std::string level_map_code;
    try
    {
        std::ifstream level_map_file(filePath);
        std::stringstream level_map_stream;

		level_map_stream << level_map_file.rdbuf();

        level_map_file.close();

		level_map_code = level_map_stream.str();
    }
    catch (std::exception e)
    {
        std::cout << "ERROR::LEVELMAP: Failed to read level map file: " << filePath << std::endl;
    }
    LevelMap level_map;
    level_map.Compile(level_map_code);
	return level_map;
}
