/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#include "common/memstream.h"
#include "common/textconsole.h"
#include "dragons/bigfile.h"
#include "dragons/cursor.h"
#include "dragons/font.h"
#include "dragons/scene.h"
#include "dragons/screen.h"
#include "dragons/dragons.h"

namespace Dragons {

Font::Font(Common::SeekableReadStream &stream, uint32 mapSize, uint32 pixelOffset, uint32 pixelSize) {
	_size = mapSize / 2;
	_map = (uint16 *)malloc(mapSize);
	if (!_map) {
		error("Allocating memory for font map.");
	}

	for (uint i = 0; i < _size; i++) {
		_map[i] = stream.readUint16LE();
	}

	_pixels = (byte *)malloc(pixelSize);
	if (!_pixels) {
		error("Allocating memory for font pixels.");
	}
	stream.seek(pixelOffset);
	stream.read(_pixels, pixelSize);
	_numChars = pixelSize / 64;
}

Font::~Font() {
	free(_map);
	free(_pixels);
}

uint16 Font::mapChar(uint16 in) {
	return _map[in - _map[0x03] + 0x05];
}

Graphics::Surface *Font::render(uint16 *text, uint16 length) {
	Graphics::Surface *surface = new Graphics::Surface();
	surface->create(length * 8, 8, Graphics::PixelFormat::createFormatCLUT8());

	renderToSurface(surface, 0, 0, text, length);

	return surface;
}

void Font::renderToSurface(Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint16 length) {
	if (x < 0 || y < 0 || x + length * 8 > surface->w || y + 8 > surface->h) {
		return;
	}
	byte *startPixelOffset = (byte *)surface->getPixels() + y * surface->pitch + x * surface->format.bytesPerPixel;
	for (int i = 0; i < length; i++) {
		byte *pixels = startPixelOffset;
		pixels += i * 8;
		byte *data = _pixels + mapChar(text[i]) * 64;
		for (int j = 0; j < 8; j++) {
			memcpy(pixels, data, 8);
			data += 8;
			pixels += surface->pitch;
		}
	}
}

FontManager::FontManager(DragonsEngine *vm, Screen *screen, BigfileArchive *bigfileArchive): _vm(vm), _screen(screen), _numTextEntries(0) {
	uint32 fileSize;
	byte *data = bigfileArchive->load("fntfiles.dat", fileSize);
	Common::SeekableReadStream *readStream = new Common::MemoryReadStream(data, fileSize, DisposeAfterUse::YES);

	_fonts[0] = loadFont(0, *readStream);
	_fonts[1] = loadFont(1, *readStream);
	_fonts[2] = loadFont(2, *readStream);

	delete readStream;

	_dat_80086f48_fontColor_flag = 0;

	_surface = new Graphics::Surface();
	_surface->create(DRAGONS_SCREEN_WIDTH, DRAGONS_SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
	clearText(); //clear backing surface.

	_boxFontChr = bigfileArchive->load("boxfont.chr", fileSize);
}

FontManager::~FontManager() {
	delete _fonts[0];
	delete _fonts[1];
	delete _fonts[2];

	_surface->free();
	delete _surface;

	free(_boxFontChr);
}

void FontManager::addText(int16 x, int16 y, uint16 *text, uint16 length, uint8 fontType) {
	assert(length < 1024);
	assert(fontType < 4);
	_fonts[fontType]->renderToSurface(_surface, x, y, text, length);
	++_numTextEntries;
}

void FontManager::draw() {
	if(_numTextEntries > 0) {
		_screen->copyRectToSurface8bpp(*_surface, _screen->getPalette(2), 0, 0, Common::Rect(_surface->w, _surface->h), false, NORMAL);
	}
}

void FontManager::clearText() {
	_numTextEntries = 0;
	_surface->fillRect(Common::Rect(_surface->w, _surface->h), 0);
}

Font *FontManager::loadFont(uint16 index, Common::SeekableReadStream &stream) {
	Common::File fd;
	if (!fd.open("dragon.exe")) {
		error("Failed to open dragon.exe");
	}
	fd.seek(_vm->getFontOffsetFromDragonEXE());
	fd.skip((index * 2)  * 28);

	fd.skip(16); //filename
	uint32 mapOffset = fd.readUint32LE();
	uint32 mapSize = fd.readUint32LE();
	fd.skip(4); //unk

	fd.skip(16); //filename
	uint32 pixelsOffset = fd.readUint32LE();
	uint32 pixelsSize = fd.readUint32LE();

	fd.close();

	stream.seek(mapOffset);
	return new Font(stream, mapSize, pixelsOffset, pixelsSize);
}

void updatePalEntry(uint16 *pal, uint16 index, uint16 newValue) {
	newValue = (uint16)(((uint16)newValue & 0x1f) << 10) | (uint16)(((uint16)newValue & 0x7c00) >> 10) |
			(newValue & 0x3e0) | (newValue & 0x8000);
	WRITE_LE_INT16(pal + index, newValue);
}

void FontManager::updatePalette() {
	uint16 *palette_f2_font_maybe = (uint16 *)_screen->getPalette(2);
	const uint16 cursor3 = 0x14a5 | 0x8000;
	if (_vm->isInMenu() || _vm->isFlagSet(ENGINE_FLAG_200)) {
		updatePalEntry(palette_f2_font_maybe, 3, cursor3); //TODO move this to palette initialisation
		if (!_vm->isUnkFlagSet(ENGINE_UNK1_FLAG_1)) {
			updatePalEntry(palette_f2_font_maybe, 16, cursor3);
		} else {
			updatePalEntry(palette_f2_font_maybe, 16, 0);
		}
		if (_vm->isUnkFlagSet(ENGINE_UNK1_FLAG_4) && _dat_80086f48_fontColor_flag != 0) {
			updatePalEntry(palette_f2_font_maybe, 17, 0x421);
		} else {
			updatePalEntry(palette_f2_font_maybe, 17, 0xfff);
		}
		updatePalEntry(palette_f2_font_maybe, 18, 0x421);
		updatePalEntry(palette_f2_font_maybe, 19, 0x3def);
		updatePalEntry(palette_f2_font_maybe, 32, cursor3);
		updatePalEntry(palette_f2_font_maybe, 49, 0xfff);
		updatePalEntry(palette_f2_font_maybe, 1, 0x8000);

		updatePalEntry(palette_f2_font_maybe, 34, 0x421);
		updatePalEntry(palette_f2_font_maybe, 35, 0x3def);
		updatePalEntry(palette_f2_font_maybe, 48, cursor3);
		updatePalEntry(palette_f2_font_maybe, 50, 0x421);
		updatePalEntry(palette_f2_font_maybe, 51, 0x3def);
		//TODO WRITE_LE_INT16(&palette_f2_font_maybe[33], READ_LE_INT16(&palette_f0[_dat_80084f58 >> 8]) & 0x7fff); //_dat_80084f58 is set in ActuallyShowMessage()
		updatePalEntry(palette_f2_font_maybe, 33, 0x3def); //temporarily put in standard gray
		if (_vm->isUnkFlagSet(ENGINE_UNK1_FLAG_1)) {
			updatePalEntry(palette_f2_font_maybe, 17, 0x3bee);
			updatePalEntry(palette_f2_font_maybe, 33, 0x3bee);
			updatePalEntry(palette_f2_font_maybe, 49, 0x3bee);
		}
	}
}

void FontManager::drawTextDialogBox(uint32 x1, uint32 y1, uint32 x2, uint32 y2) {
	const uint16 kTileBaseIndex = 1;
	const uint16 kTileIndexTop = kTileBaseIndex + 10;
	const uint16 kTileIndexBottom = kTileBaseIndex + 16;
	const uint16 kTileIndexLeft = kTileBaseIndex + 12;
	const uint16 kTileIndexRight = kTileBaseIndex + 14;
	const uint16 kTileIndexBackground = kTileBaseIndex + 13;
	const uint16 kTileIndexTopLeft = kTileBaseIndex + 9;
	const uint16 kTileIndexTopRight = kTileBaseIndex + 11;
	const uint16 kTileIndexBottomLeft = kTileBaseIndex + 15;
	const uint16 kTileIndexBottomRight = kTileBaseIndex + 17;
	// Fill background
	for (uint yc = y1 + 1; yc <= y2 - 1; ++yc) {
		for (uint xc = x1 + 1; xc <= x2 - 1; ++xc) {
			drawBoxChar(xc, yc, kTileIndexBackground);
		}
	}
	// Fill top and bottom rows
	for (uint xc = x1 + 1; xc <= x2 - 1; ++xc) {
		drawBoxChar(xc, y1, kTileIndexTop);
		drawBoxChar(xc, y2, kTileIndexBottom);
	}
	// Fill left and right columns
	for (uint yc = y1 + 1; yc <= y2 - 1; ++yc) {
		drawBoxChar(x1, yc, kTileIndexLeft);
		drawBoxChar(x2, yc, kTileIndexRight);
	}
	// Fill corners
	drawBoxChar(x1, y1, kTileIndexTopLeft);
	drawBoxChar(x2, y1, kTileIndexTopRight);
	drawBoxChar(x1, y2, kTileIndexBottomLeft);
	drawBoxChar(x2, y2, kTileIndexBottomRight);
	_numTextEntries++;
}

void FontManager::clearTextDialog(uint32 x1, uint32 y1, uint32 x2, uint32 y2) {
	debug(3, "Clear text (%d,%d) -> (%d,%d)", x1, y1, x2, y2);
//	assert(x1 > 0);
//	assert(y1 > 0);
	_surface->fillRect(Common::Rect((x1-1) * 8, (y1-1) * 8, (x2 + 1) * 8 + 1, (y2 + 1) * 8 + 1), 0);
	if (_numTextEntries > 0) {
		_numTextEntries--; //TODO need a better way to check if we should still draw the font surface.
	}
}

void FontManager::drawBoxChar(uint32 x, uint32 y, uint8 tileIndex) {
	byte *pixels = (byte *)_surface->getBasePtr(x * 8, y * 8);
	byte *data = _boxFontChr + tileIndex * 64;
	for (int j = 0; j < 8; j++) {
		memcpy(pixels, data, 8);
		data += 8;
		pixels += _surface->pitch;
	}
}

void FontManager::addAsciiText(int16 x, int16 y, const char *text, uint16 length, uint8 fontType) {
	uint16 wText[41];
	memset(wText, 0, sizeof(wText));
	if (length > 40) {
		length = 40;
	}

	for (int i = 0; i < length; i++) {
		wText[i] = text[i] & 0xFFU;
	}

	addText(x, y, wText, length, fontType);
}

} // End of namespace Dragons
