#include "extensions/textureloader.h"

#include "extensions/common/pathutils.h"
#include "legovideomanager.h"
#include "misc.h"
#include "mxdirectx/mxdirect3d.h"
#include "mxmain.h"
#include "tgl/d3drm/impl.h"

using namespace Extensions;

std::map<std::string, std::string> TextureLoaderExt::options;
std::vector<std::string> TextureLoaderExt::excludedFiles;
bool TextureLoaderExt::enabled = false;

void TextureLoaderExt::Initialize()
{
	for (const auto& option : defaults) {
		if (!options.count(option.first.data())) {
			options[option.first.data()] = option.second;
		}
	}
}

void TextureLoaderExt::AddExcludedFile(const std::string& p_file)
{
	excludedFiles.emplace_back(p_file);
}

bool TextureLoaderExt::PatchTexture(LegoTextureInfo* p_textureInfo)
{
	SDL_Surface* surface = FindTexture(p_textureInfo->m_name);
	if (!surface) {
		return false;
	}

	const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(surface->format);

	DDSURFACEDESC desc;
	memset(&desc, 0, sizeof(desc));
	desc.dwSize = sizeof(desc);
	desc.dwFlags = DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
	desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_SYSTEMMEMORY;
	desc.ddpfPixelFormat.dwSize = sizeof(desc.ddpfPixelFormat);
	desc.dwWidth = surface->w;
	desc.dwHeight = surface->h;
	desc.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS;
	desc.ddpfPixelFormat.dwRGBBitCount = details->bits_per_pixel;
	desc.ddpfPixelFormat.dwRBitMask = details->Rmask;
	desc.ddpfPixelFormat.dwGBitMask = details->Gmask;
	desc.ddpfPixelFormat.dwBBitMask = details->Bmask;
	desc.ddpfPixelFormat.dwRGBAlphaBitMask = details->Amask;

	LPDIRECTDRAW pDirectDraw = VideoManager()->GetDirect3D()->DirectDraw();
	if (pDirectDraw->CreateSurface(&desc, &p_textureInfo->m_surface, nullptr) != DD_OK) {
		SDL_DestroySurface(surface);
		return false;
	}

	memset(&desc, 0, sizeof(desc));
	desc.dwSize = sizeof(desc);

	if (p_textureInfo->m_surface->Lock(nullptr, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, nullptr) != DD_OK) {
		SDL_DestroySurface(surface);
		return false;
	}

	MxU8* dst = (MxU8*) desc.lpSurface;
	Uint8* srcPixels = (Uint8*) surface->pixels;

	if (details->bits_per_pixel == 8) {
		SDL_Palette* sdlPalette = SDL_GetSurfacePalette(surface);
		if (!sdlPalette) {
			p_textureInfo->m_surface->Unlock(desc.lpSurface);
			SDL_DestroySurface(surface);
			return false;
		}

		PALETTEENTRY entries[256];
		for (int i = 0; i < sdlPalette->ncolors; ++i) {
			entries[i].peRed = sdlPalette->colors[i].r;
			entries[i].peGreen = sdlPalette->colors[i].g;
			entries[i].peBlue = sdlPalette->colors[i].b;
			entries[i].peFlags = PC_NONE;
		}

		LPDIRECTDRAWPALETTE ddPalette = nullptr;
		if (pDirectDraw->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256, entries, &ddPalette, nullptr) != DD_OK) {
			p_textureInfo->m_surface->Unlock(desc.lpSurface);
			SDL_DestroySurface(surface);
			return false;
		}

		p_textureInfo->m_surface->SetPalette(ddPalette);
		ddPalette->Release();
	}

	memcpy(dst, srcPixels, surface->pitch * surface->h);
	p_textureInfo->m_surface->Unlock(desc.lpSurface);
	p_textureInfo->m_palette = nullptr;

	if (((TglImpl::RendererImpl*) VideoManager()->GetRenderer())
			->CreateTextureFromSurface(p_textureInfo->m_surface, &p_textureInfo->m_texture) != D3DRM_OK) {
		SDL_DestroySurface(surface);
		return false;
	}

	p_textureInfo->m_texture->SetAppData((LPD3DRM_APPDATA) p_textureInfo);
	SDL_DestroySurface(surface);
	return true;
}

SDL_Surface* TextureLoaderExt::FindTexture(const char* p_name)
{
	if (std::find(excludedFiles.begin(), excludedFiles.end(), p_name) != excludedFiles.end()) {
		return nullptr;
	}

	const char* texturePath = options["texture loader:texture path"].c_str();
	MxString relativePath = MxString(texturePath) + "/" + p_name + ".bmp";

	MxString path;
	if (!Common::ResolveGamePath(relativePath.GetData(), path)) {
		return nullptr;
	}

	return SDL_LoadBMP(path.GetData());
}
