/* Copyright (C) 1994-2004 Revolution Software Ltd
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/sword2/driver/menu.cpp,v 1.25 2004/02/05 14:19:07 eriktorbjorn Exp $
 */

#include "common/stdafx.h"
#include "sword2/sword2.h"
#include "sword2/driver/d_draw.h"
#include "sword2/driver/render.h"

namespace Sword2 {

#define MENUDEEP 40
#define MAXMENUANIMS 8

void Graphics::clearIconArea(int menu, int pocket, Common::Rect *r) {
	byte *dst;
	int i;

	r->top = menu * (RENDERDEEP + MENUDEEP) + (MENUDEEP - RDMENU_ICONDEEP) / 2;
	r->bottom = r->top + RDMENU_ICONDEEP;
	r->left = RDMENU_ICONSTART + pocket * (RDMENU_ICONWIDE + RDMENU_ICONSPACING);
	r->right = r->left + RDMENU_ICONWIDE;

	dst = _buffer + r->top * _screenWide + r->left;

	for (i = 0; i < RDMENU_ICONDEEP; i++) {
		memset(dst, 0, RDMENU_ICONWIDE);
		dst += _screenWide;
	}
}

/**
 * This function should be called regularly to process the menubar system. The
 * rate at which this function is called will dictate how smooth the menu
 * system is.
 */

void Graphics::processMenu(void) {
	byte *src, *dst;
	uint8 menu;
	uint8 i, j;
	uint8 complete;
	uint8 frameCount;
	int32 curx, xoff;
	int32 cury, yoff;
	Common::Rect r1, r2;
	int32 delta;
	static int32 lastTime = 0;

	if (lastTime == 0) {
		lastTime = _vm->_system->get_msecs();
		frameCount = 1;
	} else {
		delta = _vm->_system->get_msecs() - lastTime;
		if (delta > 250) {
			lastTime += delta;
			delta = 250;
			frameCount = 1;
		} else {
			frameCount = (uint8) ((_iconCount + 8) * delta / 750);
			lastTime += frameCount * 750 / (_iconCount + 8);
		}
	}

	while (frameCount-- > 0) {
		for (menu = RDMENU_TOP; menu <= RDMENU_BOTTOM; menu++) {
			if (_menuStatus[menu] == RDMENU_OPENING) {
				// The menu is opening, so process it here
				complete = 1;

				// Propagate the animation from the first icon.
				for (i = RDMENU_MAXPOCKETS - 1; i > 0; i--) {
					_pocketStatus[menu][i] = _pocketStatus[menu][i - 1];
					if (_pocketStatus[menu][i] != MAXMENUANIMS)
						complete = 0;
				}
				if (_pocketStatus[menu][i] != MAXMENUANIMS)
					complete = 0;

				// ... and animate the first icon
				if (_pocketStatus[menu][0] != MAXMENUANIMS)
					_pocketStatus[menu][0]++;

				// Check to see if the menu is fully open
				if (complete)
					_menuStatus[menu] = RDMENU_SHOWN;
			} else if (_menuStatus[menu] == RDMENU_CLOSING) {
				// The menu is closing, so process it here
				complete = 1;

				// Propagate the animation from the first icon.
				for (i = RDMENU_MAXPOCKETS - 1; i > 0; i--) {
					if (_icons[menu][i] && _pocketStatus[menu][i] != 0 && _pocketStatus[menu][i - 1] == 0) {
						clearIconArea(menu, i, &r1);
						updateRect(&r1);
					}

					_pocketStatus[menu][i] = _pocketStatus[menu][i - 1];
					if (_pocketStatus[menu][i] != 0)
						complete = 0;
				}
				if (_pocketStatus[menu][i] != 0)
					complete = 0;

				// ... and animate the first icon
				if (_pocketStatus[menu][0] != 0) {
					_pocketStatus[menu][0]--;

					if (_pocketStatus[menu][0] == 0) {
						clearIconArea(menu, 0, &r1);
						updateRect(&r1);
					}
				}

				// Check to see if the menu is fully closed
				if (complete)
					_menuStatus[menu] = RDMENU_HIDDEN;
			}
		}
	}
	
	// Does the menu need to be drawn?
	for (menu = RDMENU_TOP; menu <= RDMENU_BOTTOM; menu++) {
		if (_menuStatus[menu] != RDMENU_HIDDEN) {
			// Draw the menu here.
			curx = RDMENU_ICONSTART + RDMENU_ICONWIDE / 2;
			cury = (MENUDEEP / 2) + (RENDERDEEP + MENUDEEP) * menu;

			for (i = 0; i < RDMENU_MAXPOCKETS; i++) {
				if (_icons[menu][i]) {
					// Since we no longer clear the screen
					// after each frame we need to clear
					// the icon area.

					clearIconArea(menu, i, &r1);
					
					if (_pocketStatus[menu][i] == MAXMENUANIMS) {
						xoff = (RDMENU_ICONWIDE / 2);
						r2.left = curx - xoff;
						r2.right = r2.left + RDMENU_ICONWIDE;
						yoff = (RDMENU_ICONDEEP / 2);
						r2.top = cury - yoff;
						r2.bottom = r2.top + RDMENU_ICONDEEP;
					} else {
						xoff = (RDMENU_ICONWIDE / 2) * _pocketStatus[menu][i] / MAXMENUANIMS;
						r2.left = curx - xoff;
						r2.right = curx + xoff;
						yoff = (RDMENU_ICONDEEP / 2) * _pocketStatus[menu][i] / MAXMENUANIMS;
						r2.top = cury - yoff;
						r2.bottom = cury + yoff;
					}

					if (xoff != 0 && yoff != 0) {
						dst = _buffer + r2.top * _screenWide + r2.left;
						src = _icons[menu][i];

						if (_pocketStatus[menu][i] != MAXMENUANIMS) {
							squashImage(
								dst, _screenWide, r2.right - r2.left, r2.bottom - r2.top,
								src, RDMENU_ICONWIDE, RDMENU_ICONWIDE, RDMENU_ICONDEEP, NULL);
						} else {
							for (j = 0; j < RDMENU_ICONDEEP; j++) {
								memcpy(dst, src, RDMENU_ICONWIDE);
								src += RDMENU_ICONWIDE;
								dst += _screenWide;
							}
						}
						updateRect(&r1);
					}
				}
				curx += (RDMENU_ICONSPACING + RDMENU_ICONWIDE);
				r1.left += (RDMENU_ICONSPACING + RDMENU_ICONWIDE);
				r1.right += (RDMENU_ICONSPACING + RDMENU_ICONWIDE);
			}
		}
	}
}

/**
 * This function brings a specified menu into view. 
 * @param menu RDMENU_TOP or RDMENU_BOTTOM, depending on which menu to show
 * @return RD_OK, or an error code
 */

int32 Graphics::showMenu(uint8 menu) {
	// Check for invalid menu parameter
	if (menu > RDMENU_BOTTOM)
		return RDERR_INVALIDMENU;

	// Check that the menu is not currently shown, or in the process of
	// being shown.
	if (_menuStatus[menu] == RDMENU_SHOWN || _menuStatus[menu] == RDMENU_OPENING)
		return RDERR_INVALIDCOMMAND;

	_menuStatus[menu] = RDMENU_OPENING;
	return RD_OK;
}

/**
 * This function hides a specified menu.
 * @param menu RDMENU_TOP or RDMENU_BOTTOM depending on which menu to hide
 * @return RD_OK, or an error code
 */

int32 Graphics::hideMenu(uint8 menu) {
	// Check for invalid menu parameter
	if (menu > RDMENU_BOTTOM)
		return RDERR_INVALIDMENU;

	// Check that the menu is not currently hidden, or in the process of
	// being hidden.
	if (_menuStatus[menu] == RDMENU_HIDDEN || _menuStatus[menu] == RDMENU_CLOSING)
		return RDERR_INVALIDCOMMAND;

	_menuStatus[menu] = RDMENU_CLOSING;
	return RD_OK;
}

/**
 * This function hides both menus immediately.
 */

void Graphics::closeMenuImmediately(void) {
	Common::Rect r;
	int i;

	_menuStatus[0] = RDMENU_HIDDEN;
	_menuStatus[1] = RDMENU_HIDDEN;

	for (i = 0; i < RDMENU_MAXPOCKETS; i++) {
		if (_icons[0][i]) {
			clearIconArea(0, i, &r);
			updateRect(&r);
		}
		if (_icons[1][i]) {
			clearIconArea(1, i, &r);
			updateRect(&r);
		}
	}

	memset(_pocketStatus, 0, sizeof(uint8) * 2 * RDMENU_MAXPOCKETS);
}

/**
 * This function sets a menubar icon.
 * @param menu RDMENU_TOP or RDMENU_BOTTOM, depending on which menu to change
 * @param pocket the menu pocket to change
 * @param icon icon data, or NULL to clear the icon
 * @return RD_OK, or an error code
 */

int32 Graphics::setMenuIcon(uint8 menu, uint8 pocket, uint8 *icon) {
	Common::Rect r;

	// Check for invalid menu parameter.
	if (menu > RDMENU_BOTTOM)
		return RDERR_INVALIDMENU;
	
	// Check for invalid pocket parameter
	if (pocket >= RDMENU_MAXPOCKETS)
		return RDERR_INVALIDPOCKET;

	// If there is an icon in the requested menu/pocket, clear it out.
	if (_icons[menu][pocket]) {
		_iconCount--;
		free(_icons[menu][pocket]);
		_icons[menu][pocket] = NULL;
		clearIconArea(menu, pocket, &r);
		updateRect(&r);
	}

	// Only put the icon in the pocket if it is not NULL
	if (icon != NULL) {
		_iconCount++;
		_icons[menu][pocket] = (uint8 *) malloc(RDMENU_ICONWIDE * RDMENU_ICONDEEP);
		if (_icons[menu][pocket] == NULL)
			return RDERR_OUTOFMEMORY;
		memcpy(_icons[menu][pocket], icon, RDMENU_ICONWIDE * RDMENU_ICONDEEP);
	}

	return RD_OK;
}

} // End of namespace Sword2
