#include "creaturesio.h"
#include <QGuiApplication>
#include "byte_swap.h"
#include <QMessageBox>
#include <QCoreApplication>
#include <QImage>
#include "face.h"
#include "vertex.h"
#include <unordered_map>
#include "mapeditor.h"
#include <cassert>
#include <iostream>


struct image_header
{
	void read(FILE * file)
	{
		fread(&offset, 4, 1, file);
		fread(&width, 2, 1, file);
		fread(&height, 2, 1, file);

		offset = byte_swap(offset);
		width = byte_swap(width);
		height = byte_swap(height);
	}

	uint32_t offset;
	uint16_t width;
	uint16_t height;
};


struct spr_header
{
	spr_header() :
		offset(0),
		width(0),
		height(0)
	{
	}

	void read(FILE * file)
	{
		fread(&offset, 4, 1, file);
		fread(&width, 2, 1, file);
		fread(&height, 2, 1, file);

		offset = byte_swap(offset);
		width = byte_swap(width);
		height = byte_swap(height);
	}

	uint32_t offset;
	uint16_t width, height;
};


QImage ImportS16Frames(FILE * file, uint32_t RGBformat, int width, int height, int length, int img_width, int img_height, MainWindow * parent)
{
	QImage image;
	if(RGBformat > 1)
	{
		QMessageBox::information(parent, QGuiApplication::applicationDisplayName(), QObject::tr("RGB format byte has incorrect value (must be 0 or 1)."));
		return image;
	}

	if(width*height != length)
	{
		QMessageBox::information(parent, QGuiApplication::applicationDisplayName(), QObject::tr("Number of images in background file is incorrect."));
		return image;
	}

	std::vector<image_header> headers(length);

	for(int i = 0; i < length; ++i)
	{
		headers[i].read(file);
	}

	for(int i = 0; i < length; ++i)
	{
		if(headers[i].width  != img_width
		|| headers[i].height != img_height)
		{
			QMessageBox::information(parent, QGuiApplication::applicationDisplayName(), QObject::tr("Each image in background file must be %1x%2.").arg(img_width).arg(img_height));
			return image;
		}

		if(img_width == 128 && img_height == 128)
			headers[i].offset += 4;
	}


	image = QImage(width*img_width, height * img_height, QImage::Format_RGB16);
	image.fill(0);

	for(int i = 0; i < length; ++i)
	{
		fseek(file, headers[i].offset, SEEK_SET);

		for(int y = 0; y < headers[i].height; ++y)
		{
			for(int x = 0; x < headers[i].width; ++x)
			{
				uint16_t pixel;
				fread(&pixel, 2, 1, file);
				pixel = byte_swap(pixel);

				QRgb color;

				if(RGBformat)
				{
					color = 0xFF000000
						| ((pixel & 0xF800) << 8)
						| ((pixel & 0x07E0) << 5)
						| ((pixel & 0x001F) << 3);
				}
				else
				{
					color = 0xFF000000
						| ((pixel & 0x7C00) << 9)
						| ((pixel & 0x03E0) << 6)
						| ((pixel & 0x001F) << 3);
				}

				if(img_width == 128 && img_height == 128)
				{
					image.setPixel((i / height)*128 + x, (i % height)*128 + y, color);
				}
				else
				{
					image.setPixel((i / 16)*144 + x, (i % 16)*150 + y, color);
				}
			}
		}
	}

	return image;
}

QImage importS16(FILE * file, MainWindow * parent)
{
	uint32_t RGBformat;
	uint16_t length;

	fread(&RGBformat, 4, 1, file);
	fread(&length, 2, 1, file);

	RGBformat = byte_swap(RGBformat);
	length = byte_swap(length);

	return ImportS16Frames(file, RGBformat, 58, 16, length, 144, 150, parent);
}

QImage importBlk(FILE * file, MainWindow * parent)
{
	uint32_t RGBformat;
	uint16_t width, height, length;

	fread(&RGBformat, 4, 1, file);
	fread(&width, 2, 1, file);
	fread(&height, 2, 1, file);
	fread(&length, 2, 1, file);

	RGBformat = byte_swap(RGBformat);
	width = byte_swap(width);
	height = byte_swap(height);
	length = byte_swap(length);

	return ImportS16Frames(file, RGBformat, width, height, length, 128, 128, parent);
}

const QVector<QRgb> & getC1Palette()
{
	static bool initialized = false;
	const static uint8_t PALETTE_DTA[256][3] = {
			{0x00,0x00,0x00}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F},
			{0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x04,0x02,0x02},
			{0x05,0x06,0x0A}, {0x06,0x0A,0x04}, {0x06,0x09,0x0C}, {0x0B,0x04,0x02}, {0x0A,0x06,0x09}, {0x0D,0x0A,0x04},
			{0x0C,0x0B,0x0C}, {0x06,0x07,0x11}, {0x05,0x0D,0x15}, {0x06,0x0F,0x18}, {0x09,0x07,0x11}, {0x0B,0x0D,0x12},
			{0x0B,0x0E,0x1A}, {0x07,0x10,0x07}, {0x07,0x10,0x0A}, {0x0D,0x12,0x06}, {0x0D,0x12,0x0B}, {0x0F,0x18,0x06},
			{0x0F,0x18,0x0A}, {0x06,0x10,0x17}, {0x07,0x10,0x19}, {0x0D,0x11,0x14}, {0x0B,0x13,0x1A}, {0x0E,0x18,0x13},
			{0x0F,0x18,0x1C}, {0x12,0x06,0x02}, {0x12,0x07,0x09}, {0x14,0x0B,0x04}, {0x12,0x0D,0x0B}, {0x1A,0x06,0x03},
			{0x1B,0x07,0x09}, {0x1B,0x0C,0x04}, {0x1A,0x0D,0x09}, {0x12,0x0E,0x12}, {0x12,0x0E,0x1A}, {0x1A,0x0D,0x12},
			{0x1D,0x0D,0x1A}, {0x14,0x12,0x05}, {0x14,0x12,0x0C}, {0x14,0x19,0x06}, {0x13,0x1A,0x0B}, {0x1C,0x12,0x05},
			{0x1B,0x13,0x0B}, {0x1C,0x19,0x05}, {0x1D,0x19,0x0C}, {0x13,0x13,0x13}, {0x13,0x15,0x1B}, {0x15,0x19,0x14},
			{0x15,0x19,0x1C}, {0x1A,0x15,0x13}, {0x1A,0x16,0x1A}, {0x1C,0x1A,0x14}, {0x1B,0x1B,0x1B}, {0x0C,0x0F,0x21},
			{0x0E,0x17,0x24}, {0x10,0x0F,0x21}, {0x13,0x16,0x23}, {0x12,0x16,0x2C}, {0x14,0x1A,0x23}, {0x12,0x1B,0x2B},
			{0x19,0x16,0x22}, {0x19,0x17,0x2B}, {0x1B,0x1C,0x23}, {0x1B,0x1D,0x2A}, {0x13,0x17,0x31}, {0x14,0x1D,0x32},
			{0x17,0x1C,0x3B}, {0x1A,0x1E,0x33}, {0x19,0x1E,0x3D}, {0x1A,0x23,0x0D}, {0x17,0x21,0x13}, {0x17,0x20,0x1A},
			{0x1B,0x23,0x13}, {0x1D,0x22,0x1C}, {0x1E,0x29,0x13}, {0x1E,0x29,0x1A}, {0x16,0x20,0x23}, {0x17,0x20,0x2E},
			{0x1C,0x21,0x25}, {0x1D,0x22,0x2B}, {0x1F,0x29,0x23}, {0x1E,0x29,0x2C}, {0x16,0x21,0x33}, {0x16,0x24,0x39},
			{0x16,0x29,0x3C}, {0x1C,0x22,0x33}, {0x1D,0x22,0x3F}, {0x1E,0x28,0x36}, {0x1C,0x29,0x3B}, {0x23,0x06,0x04},
			{0x24,0x07,0x09}, {0x22,0x0D,0x04}, {0x23,0x0D,0x0A}, {0x2B,0x06,0x04}, {0x2B,0x07,0x08}, {0x2A,0x0C,0x04},
			{0x2B,0x0C,0x0A}, {0x26,0x0D,0x12}, {0x23,0x13,0x05}, {0x23,0x14,0x0A}, {0x24,0x1A,0x05}, {0x24,0x1A,0x0C},
			{0x2B,0x14,0x05}, {0x2A,0x15,0x0A}, {0x2C,0x1A,0x05}, {0x2B,0x1B,0x0B}, {0x22,0x15,0x12}, {0x22,0x16,0x1B},
			{0x23,0x1B,0x13}, {0x22,0x1D,0x1B}, {0x2B,0x14,0x12}, {0x2C,0x15,0x19}, {0x2A,0x1D,0x12}, {0x2B,0x1D,0x1A},
			{0x34,0x0B,0x07}, {0x35,0x0D,0x12}, {0x32,0x15,0x05}, {0x32,0x15,0x0A}, {0x33,0x1A,0x05}, {0x33,0x1C,0x0B},
			{0x3A,0x14,0x05}, {0x3A,0x14,0x0B}, {0x3A,0x1D,0x05}, {0x3A,0x1D,0x0A}, {0x33,0x14,0x12}, {0x33,0x15,0x19},
			{0x33,0x1D,0x12}, {0x32,0x1D,0x1A}, {0x3A,0x14,0x14}, {0x3B,0x16,0x18}, {0x3C,0x1C,0x12}, {0x3B,0x1C,0x1C},
			{0x24,0x0F,0x21}, {0x23,0x14,0x21}, {0x21,0x1E,0x24}, {0x21,0x1E,0x2A}, {0x2A,0x1E,0x22}, {0x29,0x1F,0x29},
			{0x20,0x1F,0x31}, {0x34,0x0C,0x20}, {0x36,0x1C,0x22}, {0x3B,0x1D,0x33}, {0x29,0x22,0x0B}, {0x25,0x21,0x14},
			{0x24,0x22,0x1C}, {0x22,0x2B,0x14}, {0x23,0x2B,0x1B}, {0x2C,0x22,0x14}, {0x2B,0x23,0x1B}, {0x2D,0x29,0x14},
			{0x2D,0x2A,0x1C}, {0x27,0x31,0x0F}, {0x29,0x34,0x17}, {0x34,0x22,0x06}, {0x34,0x22,0x0C}, {0x35,0x2A,0x05},
			{0x34,0x2A,0x0B}, {0x3C,0x23,0x05}, {0x3B,0x23,0x0B}, {0x3D,0x2B,0x05}, {0x3D,0x2B,0x0C}, {0x33,0x23,0x13},
			{0x32,0x25,0x1A}, {0x34,0x2A,0x14}, {0x34,0x2A,0x1C}, {0x3B,0x24,0x12}, {0x3B,0x24,0x19}, {0x3C,0x2B,0x13},
			{0x3B,0x2C,0x1B}, {0x34,0x31,0x0E}, {0x3D,0x33,0x03}, {0x3E,0x33,0x0C}, {0x3F,0x3C,0x03}, {0x3F,0x3B,0x0B},
			{0x35,0x31,0x14}, {0x35,0x31,0x1C}, {0x32,0x3D,0x14}, {0x33,0x3D,0x1B}, {0x3E,0x32,0x13}, {0x3D,0x33,0x1B},
			{0x3E,0x3B,0x13}, {0x3F,0x3A,0x1C}, {0x23,0x22,0x24}, {0x23,0x24,0x2B}, {0x24,0x2A,0x24}, {0x25,0x2A,0x2D},
			{0x2A,0x24,0x23}, {0x29,0x26,0x2C}, {0x2C,0x2A,0x24}, {0x2B,0x2A,0x2D}, {0x22,0x25,0x33}, {0x21,0x26,0x3E},
			{0x25,0x29,0x34}, {0x24,0x2A,0x3F}, {0x28,0x27,0x31}, {0x2B,0x2B,0x33}, {0x29,0x2E,0x3D}, {0x2A,0x32,0x2A},
			{0x26,0x31,0x31}, {0x2C,0x30,0x34}, {0x2A,0x31,0x3F}, {0x2C,0x3A,0x31}, {0x2E,0x39,0x3A}, {0x33,0x24,0x24},
			{0x32,0x26,0x29}, {0x33,0x2C,0x23}, {0x32,0x2C,0x2C}, {0x3B,0x24,0x23}, {0x3B,0x24,0x29}, {0x3A,0x2D,0x22},
			{0x3A,0x2D,0x2A}, {0x31,0x2E,0x32}, {0x31,0x2F,0x38}, {0x3D,0x2B,0x33}, {0x35,0x32,0x24}, {0x34,0x32,0x2C},
			{0x33,0x3C,0x22}, {0x33,0x39,0x2C}, {0x3C,0x33,0x24}, {0x3B,0x34,0x2B}, {0x3E,0x3A,0x24}, {0x3E,0x3B,0x2C},
			{0x35,0x32,0x33}, {0x32,0x32,0x3A}, {0x35,0x39,0x33}, {0x36,0x3A,0x39}, {0x39,0x35,0x34}, {0x38,0x34,0x38},
			{0x3C,0x3A,0x34}, {0x3D,0x3D,0x3B}, {0x3F,0x3F,0x3F}, {0x00,0x00,0x00}, {0x00,0x00,0x00}, {0x00,0x00,0x00},
			{0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F},
			{0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}, {0x3F,0x3F,0x3F}
	};

	static QVector<QRgb> palette(256);

	if(!initialized)
	{
		initialized = true;

		for(int i = 0; i < 256; ++i)
		{
			palette[i] = qRgb(PALETTE_DTA[i][0] * 4, PALETTE_DTA[i][1] * 4, PALETTE_DTA[i][2] * 4);
		}
	}


	return palette;
}


QImage importSpr(FILE * file, MainWindow * parent)
{
	QImage image;

	uint16_t length;
	fread(&length, 2, 1, file);
	length = byte_swap(length);

	if(length != 58*8)
	{
		QMessageBox::information(parent, QGuiApplication::applicationDisplayName(), QObject::tr("Creatures 1 backgrounds must contain exactly 464 images."));
		return image;
	}


	std::vector<spr_header> header(length);

	for(uint16_t i = 0; i < length; ++i)
	{
		header[i].read(file);

		if(header[i].width != 144 || header[i].height != 150)
		{
			QMessageBox::information(parent, QGuiApplication::applicationDisplayName(), QObject::tr("Each image in a creatures 1 background must be 144x150."));
			return image;
		}
	}


	image = QImage(58*144, 8*150, QImage::Format_RGB16);
	image.fill(0);

	for(uint16_t i = 0; i < length; ++i)
	{
		fseek(file, header[i].offset, SEEK_SET);

		for(int y = 0; y < header[i].height; ++y)
		{
			for(int x = 0; x < header[i].width; ++x)
			{
				uint8_t value;
				fread(&value, 1, 1, file);
				image.setPixel((i / 8)*header[i].width + x, (i % 8) * header[i].height + y, getC1Palette()[value]);
			}
		}
	}

	return image;
}

bool exportSpr(const QImage & img, FILE * file, MainWindow * window)
{
	if(img.isNull())
	{
		return false;
	}

	if(img.format() != QImage::Format_Indexed8)
	{
		return exportSpr(
			img.convertToFormat(QImage::Format_Indexed8, getC1Palette(),
				Qt::DiffuseDither | Qt::PreferDither | Qt::NoOpaqueDetection),
			file, window);
	}

	uint16_t length = byte_swap((uint16_t) (58*8));
	fwrite(&length, 2, 1, file);
	length = 58*8;

	uint32_t base_offset = 2 + 8*length;
	uint16_t width = byte_swap((uint16_t) 144);
	uint16_t height = byte_swap((uint16_t) 150);

	for(uint16_t i = 0; i < length; ++i)
	{
		uint32_t temp = byte_swap(base_offset);
		fwrite(&temp, 4, 1, file);
		fwrite(&width, 2, 1, file);
		fwrite(&height, 2, 1, file);

		base_offset += 144*150;
	}

	for(uint16_t i = 0; i < length; ++i)
	{
		int x0 = (i / 8) * 144;
		int y0 = (i % 8) * 150;

		for(int y = 0; y < 150; ++y)
		{
			for(int x = 0; x < 144; ++x)
			{
				uint8_t t = img.pixelIndex(x0 + x, y0 + y);
				fwrite(&t, 1, 1, file);
			}
		}
	}

	return true;
}

bool ExportS16Frames(FILE * file, const QImage & image, int width, int height, int img_width, int img_height, MainWindow *)
{
	int length = width*height;
	int base_offset = 6 + 8*length;
	uint16_t w = byte_swap((uint16_t) img_width);
	uint16_t h = byte_swap((uint16_t) img_height);

	for(int i = 0; i < length; ++i)
	{
		uint32_t t = byte_swap((uint32_t) base_offset);
		fwrite(&t, 4, 1, file);
		fwrite(&w, 2, 1, file);
		fwrite(&h, 2, 1, file);

		base_offset += img_width*img_height*2;
	}

	for(int i = 0; i < length; ++i)
	{
		int x0 = (i/height)*img_width;
		int y0 = (i%height)*img_height;

		for(int y = 0; y < img_height; ++y)
		{
			for(int x = 0; x < img_width; ++x)
			{
				QRgb px = image.pixel(x0 + x, y0 + y);

				uint8_t red   = qRed(px) >> 3;
				uint8_t green = qGreen(px) >> 2;
				uint8_t blue  = qBlue(px) >> 3;

				uint16_t color = (red << 11) | (green << 5) | blue;

				color = byte_swap(color);
				fwrite(&color, 2, 1, file);
			}
		}
	}

	return true;
}

bool exportS16(const QImage & image, FILE * file, MainWindow * parent)
{
	uint32_t RGBformat = byte_swap((uint32_t) 1);
	uint16_t length    = byte_swap((uint16_t) (58*16));

	fwrite(&RGBformat, 4, 1, file);
	fwrite(&length, 2, 1, file);

	return ExportS16Frames(file, image, 58, 16, 144, 150, parent);
}

bool exportBlk(const QImage & image, QSize size, FILE * file, MainWindow * parent)
{
	uint32_t RGBformat	= byte_swap((uint32_t) 1);

	uint16_t width		= (size.width() + 127) >> 7;
	uint16_t height		= (size.height() + 127) >> 7;
	uint16_t length		= byte_swap((uint16_t) (width*height));

	width		= byte_swap(width);
	height		= byte_swap(height);

	fwrite(&RGBformat, 4, 1, file);
	fwrite(&width, 2, 1, file);
	fwrite(&height, 2, 1, file);
	fwrite(&length, 2, 1, file);

	return ExportS16Frames(file, image, width, height, 128, 128, parent);
}


bool exportCos(FILE * file, QPoint position, QSize size, QString background, MapEditor * window)
{
	std::unordered_map<Face *, int> game_variables;

	fprintf(file, "setv va01 addm %i %i %i %i \"%s\"\n", position.x(), position.y(), size.width(), size.height(), background.toStdString().c_str());
	fprintf(file, "mmsc %i %i \"\"\n", position.x() + size.width()/2, position.y() + size.height()/2);

	for(auto i = window->selection.allFaces.begin(); i != window->selection.allFaces.end(); ++i)
	{
		int l  = (*i)->verticies[TopLeft	].x + position.x();
		int r  = (*i)->verticies[TopRight	].x + position.x();
		int tl = (*i)->verticies[TopLeft	].y + position.y();
		int tr = (*i)->verticies[TopRight	].y + position.y();
		int bl = (*i)->verticies[BottomLeft	].y + position.y();
		int br = (*i)->verticies[BottomRight].y + position.y();

		fprintf(file, "  setv va00 addr va01 %i %i %i %i %i %i\n", l, r, tl, tr, bl, br);
		fprintf(file, "    rtyp va00 %i\n", (*i)->room_type);
		fprintf(file, "    rmsc %i %i \"\"\n", (l + r) / 2, (tl+tr+bl+br)/4);
		fprintf(file, "    setv game \"map_tmp_%i\" va00\n", (int) game_variables.size());

		game_variables.insert(std::make_pair(*i, game_variables.size()));
	}

	int lines_printed = -1;
	int current_face = 0;

	for(auto i = window->selection.allFaces.begin(); i != window->selection.allFaces.end(); ++i, ++current_face)
	{
		for(int j = 0; j < 4; ++j)
		{
			Face * ptr = (*i)->adjacent[j];

			if(ptr == 0L) continue;

			if(++lines_printed % 10 == 0)
				fprintf(file, "\n");

			fprintf(file, "door game \"map_tmp_%i\" game \"map_tmp_%i\" %i\n", current_face, game_variables[ptr], (*i)->permeability[j]);
		}
	}

	if(lines_printed % 10 != 0)
		fprintf(file, "\n");

	for(size_t i = 0; i < game_variables.size(); ++i)
	{
		fprintf(file, "delg \"map_tmp_%i\"\n", (int) i);
	}

	return true;
}
