#include "Morault_Resources.h"
#include "Morault_Utils.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
std::map<std::string, ::Model> __models_map;
std::map<std::string, ::Texture2D> __textures_map;

Morault::Resources::_ResourceRequest Morault::Resources::load(std::string modelName)
{
    _ResourceRequest retval;
    retval.type = _ResourceRequestType::LOAD;
    retval.req = modelName;
    return retval;
}

Morault::Resources::_ResourceRequest Morault::Resources::get(std::string modelName)
{
    _ResourceRequest retval;
    retval.type = _ResourceRequestType::GET;
    retval.req = modelName;
    return retval;
}

Morault::Resources::_ResourceRequest Morault::Resources::unload(std::string modelName)
{
    _ResourceRequest retval;
    retval.type = _ResourceRequestType::UNLOAD;
    retval.req = modelName;
    return retval;
}

Morault::Resources::_ResourceRequest Morault::Resources::reload(std::string modelName)
{
    _ResourceRequest retval;
    retval.type = _ResourceRequestType::RELOAD;
    retval.req = modelName;
    return retval;
}

std::variant<Model, bool> Morault::Resources::Models(_ResourceRequest request)
{
    switch (request.type) {
    case LOAD:
        __models_map[request.req] = LoadModel(request.req.c_str());
        return true;
        break;
    case GET:
        return __models_map[request.req];
        break;
    case UNLOAD:
        UnloadModel(__models_map[request.req]);
        return true;
        break;
    case RELOAD:
        UnloadModel(__models_map[request.req]);
        __models_map[request.req] = LoadModel(request.req.c_str());
        return true;
        break;
    }
}

std::variant<Texture2D, bool> Morault::Resources::Textures(_ResourceRequest request)
{
    switch (request.type) {
    case LOAD:
        __textures_map[request.req] = LoadTexture(request.req.c_str());
        return true;
        break;
    case GET:
        if (__textures_map.find(request.req) == __textures_map.end()) __textures_map[request.req] = LoadTexture(request.req.c_str());
        return __textures_map[request.req];
        break;
    case UNLOAD:
        UnloadTexture(__textures_map[request.req]);
        return true;
        break;
    case RELOAD:
        UnloadTexture(__textures_map[request.req]);
        __textures_map[request.req] = LoadTexture(request.req.c_str());
        return true;
        break;
    }
}

Image Morault::Resources::GenTestImage()
{
	Image retval = GenImageColor(256, 256, BLANK);

	for (int y = 0; y < 256; y++) {
		for (int x = 0; x < 256; x++) {
			ImageDrawPixel(&retval, x, y, { (unsigned char)x, (unsigned char)y, (unsigned char)(255 - x), 255 });
		}
	}
	return retval;
}


Morault::Resources::WAD::WAD()
{
	std::cout << "Warning: Empty WAD instance created.\n";
}

Morault::Resources::WAD::WAD(std::string path)
{
	LoadWAD(path);
}

void Morault::Resources::WAD::LoadWAD(std::string path)
{
	loaded = false;
	lumps.clear();
	lumpCount = 0;
	std::cout << "Loading file " << path << "...\n";
	std::ifstream file(path, std::ios::binary);
	std::string filedata;
	std::stringstream filestream; filestream << file.rdbuf();
	filedata = filestream.str();
	if (
		
		
		Morault::Utils::Strings::IterativeComp(filedata, 0, "IWAD")) {
		wadType = IWAD;
		std::cout << "File is an IWAD\n";
	}
	else if (Morault::Utils::Strings::IterativeComp(filedata, 0, "PWAD")) {
		wadType = PWAD;
		std::cout << "File is a PWAD\n";
	}
	else {
		throw std::runtime_error("Invalid WAD file: Error at header start(IWAD/PWAD)\n");
	}
	lumpCount = Morault::Utils::Strings::StringIndexToInteger_4b_le(filedata, 4);

	std::cout << "File has " << lumpCount << " lumps\n";
	int directoryPointer = Morault::Utils::Strings::StringIndexToInteger_4b_le(filedata, 8);

	std::string directory = Morault::Utils::Strings::IterativeStringExcerpt(filedata, directoryPointer, 16 * lumpCount);

	for (int i = 0; i < lumpCount; i++) {
		Lump lump;
		std::string lumpIndex = Morault::Utils::Strings::IterativeStringExcerpt(directory, i * 16, 16);
		int lumpPointer = Morault::Utils::Strings::StringIndexToInteger_4b_le(lumpIndex, 0);
		int lumpSize = Morault::Utils::Strings::StringIndexToInteger_4b_le(lumpIndex, 4);
		lump.name = "", lump.data = "";
		for (int j = 0; j < 8; j++) {
			if (lumpIndex.at(8 + j) != 0x0) lump.name += lumpIndex.at(8 + j);
		}

		lump.data = Morault::Utils::Strings::IterativeStringExcerpt(filedata, lumpPointer, lumpSize);

		lumps.push_back(lump);

		std::cout << "(" << i << "/" << lumpCount << ")" << "Loaded lump \"" << lump.name << "\" of size " << lumpSize << "\n";
	}
	loaded = true;
}

Morault::Resources::WAD::~WAD()
{
}