/* 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "lure/lure.h"
#include "lure/palette.h"
#include "common/util.h"

namespace Lure {

// Constructor
// Defaults the palette to a full 256 entry palette

Palette::Palette() {
	_numEntries = GAME_COLORS;
	_palette = Memory::allocate(_numEntries * 4);
	_palette->empty();
}

// Consructor
// Sets up a palette with the given number of entries and a copy of the passed data

Palette::Palette(uint16 srcNumEntries, const byte *srcData, PaletteSource paletteSource) {
	_numEntries = srcNumEntries;
	_palette = Memory::allocate(_numEntries * 4);

	if (srcData) {
		if (paletteSource == RGB64)
			convertRgb64Palette(srcData, _numEntries);
		else if (paletteSource == EGA) {
			assert((srcNumEntries == 16) || (srcNumEntries == 17));
			convertEGAPalette(srcData);
		} else
			_palette->copyFrom(srcData, 0, 0, _numEntries * 4);

	} else {
		// No data provided, set a null palette
		_palette->empty();
	}
}

// Constructor
// Makes a copy of a passed palette object

Palette::Palette(Palette &src) {
	_numEntries = src.numEntries();
	_palette = Memory::duplicate(src._palette);
}

// Constructor
// Loads a palette from a resource

Palette::Palette(uint16 resourceId, PaletteSource paletteSource) {
	Disk &disk = Disk::getReference();
	bool isEGA = LureEngine::getReference().isEGA();
	MemoryBlock *srcData = disk.getEntry(resourceId);

	if (paletteSource == DEFAULT)
		paletteSource = isEGA ? EGA : RGB64;

	switch (paletteSource) {
	case EGA:
		// Handle EGA palette
		if ((srcData->size() != 16) && (srcData->size() != 17))
			error("Specified resource %d is not a palette", resourceId);

		_numEntries = 16;
		_palette = Memory::allocate(_numEntries * 4);
		convertEGAPalette(srcData->data());
		break;

	case RGB64:
		if (((srcData->size() % 3) != 0) || ((srcData->size() / 3) > GAME_COLORS))
			error("Specified resource %d is not a palette", resourceId);

		_numEntries = srcData->size() / 3;
		_palette = Memory::allocate(_numEntries * 4);
		convertRgb64Palette(srcData->data(), _numEntries);
		break;

	default:
		error("Invalid palette type specified for palette resource");
	}

	delete srcData;
}

// Destructor

Palette::~Palette() {
	delete _palette;
}

void Palette::convertRgb64Palette(const byte *srcPalette, uint16 srcNumEntries) {
	byte *pDest = _palette->data();
	const byte *pSrc = srcPalette;

	while (srcNumEntries-- > 0) {
		*pDest++ = (pSrc[0] << 2) + (pSrc[0] >> 4);
		*pDest++ = (pSrc[1] << 2) + (pSrc[1] >> 4);
		*pDest++ = (pSrc[2] << 2) + (pSrc[2] >> 4);
		*pDest++ = 0;
		pSrc += 3;
	}
}

// EGA palette definition copied from DOSBox 0.72
static byte ega_palette[64][3] =
{
  {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
  {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
  {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
  {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
  {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
  {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
  {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
  {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f}
};

void Palette::convertEGAPalette(const byte *srcPalette) {
	byte *pDest = _palette->data();
	const byte *pSrc = srcPalette;

	for (int index = 0; index < 16; ++index, ++pSrc) {
		// Handle RGB components of entry
		assert(*pSrc < 64);
		byte *v = &ega_palette[*pSrc][0];
		*pDest++ = *v++ * 4;
		*pDest++ = *v++ * 4;
		*pDest++ = *v++ * 4;
		*pDest++ = 0;
	}
}

void Palette::setEntry(uint8 index, uint32 value) {
	if (index >= numEntries()) error("Invalid palette index: %d", index);
	uint32 *entry = (uint32 *) (data() + index * 4);
	*entry = value;
}

uint32 Palette::getEntry(uint8 index) {
	if (index >= numEntries()) error("Invalid palette index: %d", index);
	uint32 *entry = (uint32 *) (data() + index * 4);
	return *entry;
}

void Palette::copyFrom(Palette *src) {
	_palette->copyFrom(src->palette());
}

/*--------------------------------------------------------------------------*/

PaletteCollection::PaletteCollection(uint16 resourceId) {
	Disk &d = Disk::getReference();
	MemoryBlock *resource = d.getEntry(resourceId);
	bool isEGA = LureEngine::getReference().isEGA();
	uint32 palSize;
	uint8 *data = resource->data();

	if (isEGA) {
		// EGA Palette collection - only has 1 sub-palette
		if ((resource->size() != 16) && (resource->size() != 17))
			error("Resource #%d is not a valid palette set", resourceId);

		_numPalettes = 1;
		_palettes = (Palette **) Memory::alloc(1 * sizeof(Palette *));
		_palettes[0] = new Palette(16, data, EGA);

	} else {
		// VGA Palette collection
		if (resource->size() % (SUB_PALETTE_SIZE * 3) != 0)
			error("Resource #%d is not a valid palette set", resourceId);

		palSize = SUB_PALETTE_SIZE * 3;
		_numPalettes = resource->size() / palSize;

		_palettes = (Palette **) Memory::alloc(_numPalettes * sizeof(Palette *));
		for (uint8 paletteCtr = 0; paletteCtr < _numPalettes; ++paletteCtr, data += palSize)
			_palettes[paletteCtr] = new Palette(SUB_PALETTE_SIZE, data, RGB64);
	}

	delete resource;
}

PaletteCollection::~PaletteCollection() {
	for (int paletteCtr = 0; paletteCtr < _numPalettes; ++paletteCtr)
		delete _palettes[paletteCtr];
	free(_palettes);
}

Palette &PaletteCollection::getPalette(uint8 paletteNum) {
	if (paletteNum >= _numPalettes)
		error("Invalid palette index specified");
	return *_palettes[paletteNum];
}

} // End of namespace Lure