/* 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 "common/scummsys.h"
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/display_client.h"
#include "backends/platform/psp/default_display_client.h"
#include "backends/platform/psp/cursor.h"

//#define __PSP_DEBUG_FUNCS__	/* For debugging the stack */
//#define __PSP_DEBUG_PRINT__

#include "backends/platform/psp/trace.h"

void Cursor::init() {
	DEBUG_ENTER_FUNC();

	_renderer.setBuffer(&_buffer);			// We do this explicitly
	_renderer.setPalette(&_screenPalette);	// because we want to choose screenpalette by default
	_renderer.setUseGlobalScaler(true);
	setRendererModePalettized(true);		// Assume we start in 8bit mode

	// Default modes
	_palette.setPixelFormats(PSPPixelFormat::Type_5551, PSPPixelFormat::Type_Palette_8bit); // default
	_screenPalette.setPixelFormats(PSPPixelFormat::Type_5551, PSPPixelFormat::Type_Palette_8bit);
	_buffer.setPixelFormat(PSPPixelFormat::Type_5551);
}

void Cursor::deallocate() {
	DEBUG_ENTER_FUNC();

	_buffer.deallocate();
	_palette.deallocate();
	_screenPalette.deallocate();
}

void Cursor::setCursorPalette(const byte *colors, uint start, uint num) {
	DEBUG_ENTER_FUNC();

	if (!_palette.isAllocated()) {
		_palette.allocate();
	}

	// Workaround: This is wrong, but we seem to not be getting setScreenPalette
	if (!_screenPalette.isAllocated()) {
		_screenPalette.allocate();
	}

	_palette.setPartial(colors, start, num);
	setDirty();
}

void Cursor::setScreenPalette(const byte *colors, uint start, uint num) {
	DEBUG_ENTER_FUNC();

	if (!_screenPalette.isAllocated()) {
		_screenPalette.allocate();
	}

	_screenPalette.setPartial(colors, start, num);
	setDirty();
}

void Cursor::setKeyColor(uint32 color) {
	DEBUG_ENTER_FUNC();
	PSP_DEBUG_PRINT("new color[%u], old color[%u]\n", color, _keyColor);

	// If it's a different color, undo the last keycolor
	if (_buffer.hasPalette() && color != _keyColor) {
		if (_screenPalette.isAllocated())
			_screenPalette.setColorPositionAlpha(_keyColor, true);
		if (_palette.isAllocated())
			_palette.setColorPositionAlpha(_keyColor, true);
	}
	// Don't need anything special for 16-bit
	_keyColor = color;
}

void Cursor::clearKeyColor() {
	DEBUG_ENTER_FUNC();
	PSP_DEBUG_PRINT("keyColor[%d]\n", _keyColor);

	// We need 2 mechanisms: one for palettized and one for 16 bit
	if (_buffer.hasPalette()) {
		if (_screenPalette.isAllocated())
			_screenPalette.setColorPositionAlpha(_keyColor, false);		// set keycolor to 0
		if (_palette.isAllocated())
			_palette.setColorPositionAlpha(_keyColor, false);
	} else {	// 16bit
		_renderer.setKeyColor(_keyColor);
	}
	setDirty();
}

void Cursor::enableCursorPalette(bool enable) {
	DEBUG_ENTER_FUNC();
	PSP_DEBUG_PRINT("enable[%s]\n", enable ? "true" : "false");

	_useCursorPalette = enable;
	if (enable)
		_renderer.setPalette(&_palette);	// very important that we do this switch
	else
		_renderer.setPalette(&_screenPalette);

	setDirty();
}

inline void Cursor::setSize(uint32 width, uint32 height) {
	DEBUG_ENTER_FUNC();
	PSP_DEBUG_PRINT("width[%u], height[%u]\n", width, height);

	_buffer.setSize(width, height, Buffer::kSizeByTextureSize);	// we'll use texture size for mouse
	_renderer.setDrawWholeBuffer();		// We need to let the renderer know how much to draw
}

void Cursor::copyFromArray(const byte *array) {
	DEBUG_ENTER_FUNC();

	if (!_buffer.isAllocated())	{
		_buffer.allocate();
	}

	_buffer.copyFromArray(array, _buffer.getSourceWidthInBytes());	// pitch is source width
	setDirty();

	// debug
	//PSP_DEBUG_DO(_buffer.print(0xF));
}

void Cursor::setHotspot(int32 x, int32 y) {
	DEBUG_ENTER_FUNC();

	_hotspotX = x;
	_hotspotY = y;
	updateRendererOffset();	// Important

	PSP_DEBUG_PRINT("hotspotX[%d], hotspotY[%d]\n", x, y);
}

// Returns true if change in x or y
bool Cursor::increaseXY(int32 incX, int32 incY) {
	DEBUG_ENTER_FUNC();

	int32 oldX = _x, oldY = _y;

	// adjust for differences in X and Y
	adjustXYForScreenSize(incX, incY);

	_x += incX;
	_y += incY;

	// Clamp mouse
	if (_x < 0)
		_x = 0;
	if (_y < 0)
		_y = 0;
	if (_x >= (int)_mouseLimitWidth)
		_x = (int)_mouseLimitWidth - 1;
	if (_y >= (int)_mouseLimitHeight)
		_y = (int)_mouseLimitHeight - 1;

	PSP_DEBUG_PRINT("X[%d], Y[%d]\n", _x, _y);

	if (oldX != _x || oldY != _y) {
		updateRendererOffset();
		setDirty();
		return true;
	}

	return false;
}

// Set limits on the movement of the cursor ie. screen size
void Cursor::setLimits(uint32 width, uint32 height) {
#define PSP_SCREEN_WIDTH 480
#define PSP_SCREEN_HEIGHT 272
	DEBUG_ENTER_FUNC();

	PSP_DEBUG_PRINT("width[%u], height[%u]\n", width, height);
	_mouseLimitWidth = width;
	_mouseLimitHeight = height;
}

// Adjust X,Y movement for the screen size to keep it consistent
inline void Cursor::adjustXYForScreenSize(int32 &x, int32 &y) {
	DEBUG_ENTER_FUNC();
	// We have our speed calibrated for the y axis at 480x272. The idea is to adjust this for other
	// resolutions and for x, which is wider.
	int32 newX = x, newY = y;

	// adjust width movement to match height (usually around 1.5)
	if (_mouseLimitWidth >= _mouseLimitHeight + (_mouseLimitHeight >> 1))
		newX = newX + (newX >> 1);

	if (_mouseLimitWidth >= 600) {	// multiply by 2
		newX <<= 1;
		newY <<= 1;
	} else if (_mouseLimitWidth >= 480) {	// multiply by 1.5
		newX = newX + (newX >> 1);
		newY = newY + (newY >> 1);
	}

	// Divide all movements by 8
	newX >>= 3;
	newY >>= 3;

	// Make sure we didn't destroy minimum movement
	if (!((x && !newX) || (y && !newY))) {
		x = newX;
		y = newY;
	}
}

// This is only called when we have a new screen
void Cursor::setScreenPaletteScummvmPixelFormat(const Graphics::PixelFormat *format) {
	DEBUG_ENTER_FUNC();

	uint32 oldPaletteSize = 0;
	if (_screenPalette.isAllocated())
		oldPaletteSize = _screenPalette.getSizeInBytes();

	PSPPixelFormat::Type bufferType = PSPPixelFormat::Type_Unknown;
	PSPPixelFormat::Type paletteType = PSPPixelFormat::Type_Unknown;
	bool swapRedBlue = false;

	// Convert Scummvm Pixel Format to PSPPixelFormat
	PSPPixelFormat::convertFromScummvmPixelFormat(format, bufferType, paletteType, swapRedBlue);

	if (paletteType == PSPPixelFormat::Type_None) {
		//_screenPalette.deallocate();		// leave palette for default CLUT8
		setRendererModePalettized(false);	// use 16-bit mechanism
	} else {	// We have a palette
		_screenPalette.setPixelFormats(paletteType, bufferType);
		_palette.setPixelFormats(paletteType, bufferType);
		setRendererModePalettized(true);	// use palettized mechanism
	}
}

// This is called many many times
void Cursor::setSizeAndScummvmPixelFormat(uint32 width, uint32 height, const Graphics::PixelFormat *format) {
	DEBUG_ENTER_FUNC();

	PSP_DEBUG_PRINT("useCursorPalette[%s]\n", _useCursorPalette ? "true" : "false");

	uint32 oldBufferSize = 0, oldPaletteSize = 0;

	if (_buffer.isAllocated())
		oldBufferSize = _buffer.getSizeInBytes();

	if (_palette.isAllocated())
		oldPaletteSize = _palette.getSizeInBytes();

	setSize(width, height);

	PSPPixelFormat::Type bufferType = PSPPixelFormat::Type_Unknown;
	PSPPixelFormat::Type paletteType = PSPPixelFormat::Type_Unknown;
	bool swapRedBlue = false;

	PSPPixelFormat::convertFromScummvmPixelFormat(format, bufferType, paletteType, swapRedBlue);
	PSP_DEBUG_PRINT("bufferType[%u], paletteType[%u]\n", bufferType, paletteType);

	// Check if we need to set new pixel format
	if (_buffer.getPixelFormat() != bufferType) {
		PSP_DEBUG_PRINT("new buffer pixel format[%u] is different from [%u]. Setting it.\n", bufferType, _buffer.getPixelFormat());
		_buffer.setPixelFormat(bufferType);
	}

	// Check if we need to reallocate
	if (_buffer.getSizeInBytes() != oldBufferSize) {
		_buffer.allocate();
		PSP_DEBUG_PRINT("reallocating buffer. new size: width[%u], height[%u]\n", width, height);
	}

	PSP_DEBUG_PRINT("palette pixel format[%u]\n", paletteType);

	if (paletteType == PSPPixelFormat::Type_None) {
		setRendererModePalettized(false);	// use palettized mechanism
	} else {	// We have a palette
		_palette.setPixelFormats(paletteType, bufferType);
		setRendererModePalettized(true);	// use palettized mechanism
	}

	// debug
	// PSP_DEBUG_DO(_palette.print(10));
	// PSP_DEBUG_DO(_screenPalette.print(10));
}

void Cursor::setXY(int x, int y) {
	DEBUG_ENTER_FUNC();

	_x = x;
	_y = y;
	updateRendererOffset();	// Very important to let renderer know things changed
	setDirty();
}

inline void Cursor::updateRendererOffset() {
	DEBUG_ENTER_FUNC();
	_renderer.setOffsetOnScreen(_x - _hotspotX, _y - _hotspotY);
}

inline void Cursor::setRendererModePalettized(bool palettized) {
	if (palettized) {	// We have a palette. Use blending
		_renderer.setAlphaBlending(true);
		_renderer.setAlphaReverse(false);
		_renderer.setColorTest(false);
	} else {			// 16 bits, no palette
		// Color test is an easy way for the hardware to make our keycolor
		// transparent.
		_renderer.setColorTest(true);

		// Alpha blending is not strictly required, but makes the cursor look
		// much better
		_renderer.setAlphaBlending(true);

		// Pixel formats without alpha (5650) are considered to have their alpha set.
		// Since pixel formats with alpha don't have their alpha bits set, we reverse
		// the alpha format for them so that 0 alpha is 1.
		if (_buffer.getPixelFormat() != PSPPixelFormat::Type_5650)
			_renderer.setAlphaReverse(true);
		else
			_renderer.setAlphaReverse(false);
	}
}
