#include "Images.hpp"

using namespace render;

std::unique_ptr<Texture> (*Texture::loadFunc)(const std::string&, const sampling&) = nullptr;
std::unique_ptr<TextureCube> (*TextureCube::loadFunc)(const std::array<std::string, 6>&, const Texture::sampling &) = nullptr;
std::unique_ptr<TextureArray> (*TextureArray::loadFunc)(const std::vector<std::string>&, const Texture::sampling &) = nullptr;

#include <stdio.h>
#include <string.h>
#include <string>
#include <algorithm>

#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII

std::optional<Image::properties> Image::Read(const std::string& imagepath, std::vector<unsigned char>& data, bool srgb) {

	unsigned char header[124];
    properties info;

    FILE *fp;

	/* try to open the file */
	fp = fopen(imagepath.c_str(), "rb");
	if (fp == NULL){
		printf("%s could not be opened.\n", imagepath.c_str()); getchar();
		return {};
	}

	/* verify the type of file */
	char filecode[4];
	fread(filecode, 1, 4, fp);
	if (strncmp(filecode, "DDS ", 4) != 0) {
		fclose(fp);
		return {};
	}

	/* get the surface desc */
	fread(&header, 124, 1, fp);

	info.size.height      = *(unsigned int*)&(header[8 ]);
	info.size.width	     = *(unsigned int*)&(header[12]);
	unsigned int linearSize	 = *(unsigned int*)&(header[16]);
	info.mipmapLevels = *(unsigned int*)&(header[24]);
	unsigned int fourCC      = *(unsigned int*)&(header[80]);


	/* how big is it going to be including all mipmaps? */
	unsigned int bufsize = info.mipmapLevels > 1 ? linearSize * 2 : linearSize;
	data.resize(bufsize);
	fread(data.data(), 1, bufsize, fp);
	/* close the file pointer */
	fclose(fp);

	switch(fourCC)
	{
	case FOURCC_DXT3:
		info.format = srgb ? Format::BC2 : Format::BC2_UNORM;
		break;
	case FOURCC_DXT5:
		info.format = srgb ? Format::BC3 : Format::BC3_UNORM;
		break;
		//MAYBE: VK_FORMAT_BC6H_SFLOAT_BLOCK
	default:
		return {};
	}
	//WONT-FIX: miplevels with size < block size (2 last) are corrupted
	const uint32_t maxMipmapLevels = 1 + std::floor(std::log2(std::max(info.size.height, info.size.width))) - 2;
	info.mipmapLevels = std::min(maxMipmapLevels, info.mipmapLevels);

	return info;
}