/* 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 "hdb/hdb.h"
#include "hdb/ai.h"
#include "hdb/gfx.h"
#include "hdb/input.h"
#include "hdb/map.h"
#include "hdb/sound.h"
#include "hdb/menu.h"
#include "hdb/window.h"

namespace HDB {

void Input::init() {
	_stylusDown = false;
	_buttons = 0;

	_keyUp = Common::KEYCODE_UP;
	_keyDown = Common::KEYCODE_DOWN;
	_keyLeft = Common::KEYCODE_LEFT;
	_keyRight = Common::KEYCODE_RIGHT;
	_keyMenu = Common::KEYCODE_ESCAPE;
	_keyUse = Common::KEYCODE_RETURN;
	_keyInv = Common::KEYCODE_SPACE;
	_keyDebug = Common::KEYCODE_F1;
	_keyQuit = Common::KEYCODE_F10;

	_mouseX = g_hdb->_screenWidth / 2;
	_mouseY = g_hdb->_screenHeight / 2;

	_mouseLButton = _mouseMButton = _mouseRButton = 0;
}

void Input::setButtons(uint16 b) {
	_buttons = b;
	if (!b)
		return;

	// Change Game State
	if ((_buttons & kButtonA) && (g_hdb->getGameState() != GAME_MENU)) {
		if (g_hdb->_ai->cinematicsActive() && g_hdb->_ai->cineAbortable()) {
			g_hdb->_ai->cineAbort();
			g_hdb->_sound->playSound(SND_POP);
			return;
		}

		if (g_hdb->getGameState() == GAME_TITLE)
			g_hdb->_menu->changeToMenu();
		g_hdb->_sound->playSound(SND_MENU_BACKOUT);
		g_hdb->changeGameState();
	}

	if (g_hdb->isPPC()) {
		if (_buttons & kButtonD) {
			if (g_hdb->_window->inventoryActive()) {
				g_hdb->_window->closeInv();
				g_hdb->_window->openDeliveries(false);
			} else if (g_hdb->_window->deliveriesActive()) {
				g_hdb->_window->closeDlvs();
			} else {
				g_hdb->_window->openInventory();
			}
			return;
		}
	}

	// Debug Mode Cycling
	if ((_buttons & kButtonExit) && g_hdb->getCheatingOn()) {
		int	debugFlag = g_hdb->getDebug();
		debugFlag++;
		if (debugFlag > 2)
			debugFlag = 0;
		g_hdb->setDebug(debugFlag);

		if (debugFlag == 2)
			g_hdb->_ai->clearWaypoints();

		if (!debugFlag && g_hdb->getGameState() == GAME_PLAY) {
			int	x, y;
			g_hdb->_ai->getPlayerXY(&x, &y);
			g_hdb->_map->centerMapXY(x + 16, y + 16);	// point to center of player
		}
	}

	if (g_hdb->getGameState() == GAME_PLAY) {
		// Is Player Dead? Click on TRY AGAIN
		if (g_hdb->_ai->playerDead()) {
			// TRY AGAIN is onscreen...
			if (_buttons & kButtonB) {
				if (g_hdb->loadGameState(kAutoSaveSlot).getCode() == Common::kNoError) {
					g_hdb->_window->clearTryAgain();
					g_hdb->setGameState(GAME_PLAY);
				}
			}
			return;
		}

		if (g_hdb->isPPC()) {
			// Deliveries screen?
			if (g_hdb->_window->deliveriesActive() && !g_hdb->_window->animatingDelivery()) {
				if (_buttons & kButtonLeft) {
					int	amount = g_hdb->_ai->getDeliveriesAmount();
					int	current = g_hdb->_window->getSelectedDelivery();

					if (!current)
						current = amount - 1;
					else
						current--;

					g_hdb->_window->setSelectedDelivery(current);
				} else if (_buttons & kButtonRight) {
					int amount = g_hdb->_ai->getDeliveriesAmount();
					int current = g_hdb->_window->getSelectedDelivery();

					current++;
					if (current == amount)
						current = 0;

					g_hdb->_window->setSelectedDelivery(current);
				} else if (_buttons & kButtonB)
					g_hdb->_window->closeDlvs();
				return;
			}

			//
			// Resources screen?  Move select cursor around
			//
			if (g_hdb->_window->inventoryActive()) {
				// select weapon?
				if (_buttons & kButtonB) {
					static AIType lastWeaponSelected = AI_NONE;

					if (!g_hdb->getActionMode()) {
						g_hdb->_window->closeInv();
						return;
					}

					AIType t = g_hdb->_ai->getInvItemType(g_hdb->_window->getInvSelect());
					Tile *gfx = g_hdb->_ai->getInvItemGfx(g_hdb->_window->getInvSelect());

					switch (t) {
					case ITEM_CLUB:
					case ITEM_ROBOSTUNNER:
					case ITEM_SLUGSLINGER:
						g_hdb->_ai->setPlayerWeapon(t, gfx);
						if (t == lastWeaponSelected) {
							g_hdb->_window->closeInv();
							return;
						}
						lastWeaponSelected = t;
						g_hdb->_sound->playSound(SND_MENU_ACCEPT);
						return;
					default:
						break;
					}
					g_hdb->_sound->playSound(SND_CELLHOLDER_USE_REJECT);
					return;
				}


				if (_buttons & kButtonLeft) {
					int	amount = g_hdb->_ai->getInvMax();
					int	current = g_hdb->_window->getInvSelect();

					if (!amount)
						return;

					if (current == 5)
						current = amount - 1;
					else if (!current && amount > 5)
						current = 4;
					else if (!current)
						current = amount - 1;
					else
						current--;

					g_hdb->_sound->playSound(SND_MENU_SLIDER);
					g_hdb->_window->setInvSelect(current);
				} else if (_buttons & kButtonRight) {
					int amount = g_hdb->_ai->getInvMax();
					int current = g_hdb->_window->getInvSelect();

					if (!amount)
						return;

					if (amount > 5) {
						if (current == amount - 1)
							current = 5;
						else if (current == 4)
							current = 0;
						else
							current++;
					} else if (current == amount - 1)
						current = 0;
					else
						current++;

					g_hdb->_sound->playSound(SND_MENU_SLIDER);
					g_hdb->_window->setInvSelect(current);
				} else if (_buttons & kButtonUp) {
					int	amount = g_hdb->_ai->getInvMax();
					int	current = g_hdb->_window->getInvSelect();

					if (amount < 6)
						return;

					if (current - 5 >= 0)
						current -= 5;

					g_hdb->_sound->playSound(SND_MENU_SLIDER);
					g_hdb->_window->setInvSelect(current);
				} else if (_buttons & kButtonDown) {
					int amount = g_hdb->_ai->getInvMax();
					int current = g_hdb->_window->getInvSelect();

					if (amount < 6)
						return;

					if (current + 5 < amount)
						current += 5;
					else if (current < 5)
						current = amount - 1;

					g_hdb->_sound->playSound(SND_MENU_SLIDER);
					g_hdb->_window->setInvSelect(current);
				}
				return;
			}
		}

		// Choose from DialogChoice
		if (g_hdb->_window->dialogChoiceActive()) {
			if (_buttons & kButtonUp)
				g_hdb->_window->dialogChoiceMoveup();
			else if (_buttons & kButtonDown)
				g_hdb->_window->dialogChoiceMovedown();
			else if (_buttons & kButtonB)
				g_hdb->_window->closeDialogChoice();
			return;
		}

		// Try to move the player
		if (!g_hdb->_ai->playerDead())
			g_hdb->_ai->movePlayer(_buttons);
	}
}

uint16 Input::getButtons() {
	return _buttons;
}

void Input::stylusDown(int x, int y) {
	static uint32 delay = 0, time;

	// Don't let the screen get clicked too fast
	time = g_system->getMillis();
	if (time - delay < 100)
		return;
	time = delay;

	_stylusDown = true;
	_stylusDownX = x;
	_stylusDownY = y;
	GameState gs = g_hdb->getGameState();

	switch (gs) {
	case GAME_TITLE:
		g_hdb->_menu->changeToMenu();
		g_hdb->changeGameState();
		break;
	case GAME_MENU:
		g_hdb->_menu->processInput(x, y);
		break;
	case GAME_PLAY:
		{
		// Is Player Dead? Click on TRY AGAIN
		if (g_hdb->_ai->playerDead()) {
			if (y >= g_hdb->_window->_tryRestartY && y <= g_hdb->_window->_tryRestartY + 24) {
				if (g_hdb->loadGameState(kAutoSaveSlot).getCode() == Common::kNoError) {
					g_hdb->_window->clearTryAgain();
					g_hdb->setGameState(GAME_PLAY);
				}
			}
			return;
		}

		if (g_hdb->isPPC()) {
			// is Deliveries active?
			if (g_hdb->_window->deliveriesActive()) {
				if (!g_hdb->_window->checkDlvsClose(x, y))
					return;
				if (!g_hdb->_ai->cinematicsActive())
					return;
			}

			// is Inventory active?
			if (g_hdb->_window->inventoryActive()) {
				if (!g_hdb->_window->checkInvClose(x, y))
					return;
				if (!g_hdb->_ai->cinematicsActive())
					return;
			}
		}

		// Is Dialog Active?
		if (g_hdb->_window->dialogActive()) {
			g_hdb->_window->closeDialog();
			if (!g_hdb->_ai->cinematicsActive())
				return;
		}

		// Is a Choice Dialog Active?
		if (g_hdb->_window->dialogChoiceActive()) {
			if (!g_hdb->_window->checkDialogChoiceClose(x, y))
				return;
			if (!g_hdb->_ai->cinematicsActive())
				return;
		}

		// Is MessageBar active?
		if (g_hdb->_window->msgBarActive()) {
			if (g_hdb->_window->checkMsgClose(x, y))
				return;
		}

		// In a cinematic?
		if (g_hdb->_ai->playerLocked())
			return;

		// Check for map dragging in debug Mode and place player there
		if ((GAME_PLAY == g_hdb->getGameState()) && g_hdb->getDebug() == 2) {
			int mx, my;
			g_hdb->_map->getMapXY(&mx, &my);

			mx = ((mx + _stylusDownX) / kTileWidth) * kTileWidth;
			my = ((my + _stylusDownY) / kTileHeight) * kTileHeight;
			g_hdb->_ai->setPlayerXY(mx, my);

			g_hdb->startMoveMap(x, y);
			return;
		}

		// Clicked in the world
		int worldX, worldY;
		g_hdb->_map->getMapXY(&worldX, &worldY);
		worldX = ((worldX + x) / kTileWidth) * kTileWidth;
		worldY = ((worldY + y) / kTileHeight) * kTileHeight;

		if (!g_hdb->isPPC()) {
			// Don't allow a click into INV/DELIVERIES area to go into the world
			if (x >= (g_hdb->_screenWidth - 32 * 5))
				return;
		}

		// Double-Clicking on the player to open inventory?
		int nx, ny;
		g_hdb->_ai->getPlayerXY(&nx, &ny);
		if (g_hdb->isPPC()) {
			if (nx == worldX && ny == worldY) {
				static uint32 dblClickTimer = 0;

				if (dblClickTimer && ((int)(g_system->getMillis() - dblClickTimer) < (int)(kGameFPS * 1000 / 60))) {
					g_hdb->_window->openInventory();
					dblClickTimer = 0;
					g_hdb->_ai->togglePlayerRunning();
					if (g_hdb->_ai->playerRunning())
						g_hdb->_window->centerTextOut("Running Speed", g_hdb->_screenHeight - 32, kRunToggleDelay * kGameFPS);
					else
						g_hdb->_window->centerTextOut("Walking Speed", g_hdb->_screenHeight - 32, kRunToggleDelay * kGameFPS);
					g_hdb->_sound->playSound(SND_SWITCH_USE);
					return;
				} else
					dblClickTimer = g_system->getMillis();
			}
		}

		// Toggle Walk Speed if we clicked Player
		static uint32 lastRunning = g_system->getMillis();
		if (nx == worldX && ny == worldY) {
			if (lastRunning > g_system->getMillis())
				return;
			lastRunning = g_system->getMillis() + 1000 * kRunToggleDelay;
			g_hdb->_ai->togglePlayerRunning();
			if (g_hdb->_ai->playerRunning())
				g_hdb->_window->centerTextOut("Running Speed", g_hdb->_screenHeight - 32, kRunToggleDelay * kGameFPS);
			else
				g_hdb->_window->centerTextOut("Walking Speed", g_hdb->_screenHeight - 32, kRunToggleDelay * kGameFPS);
			g_hdb->_sound->playSound(SND_SWITCH_USE);
		}

		g_hdb->setTargetXY(worldX, worldY);
		break;
		}
	case GAME_LOADING:
		break;
	}
}

void Input::stylusUp(int x, int y) {
	_stylusDown = false;
}

void Input::stylusMove(int x, int y) {
	// In a cinematic?
	if (g_hdb->_ai->playerLocked() || g_hdb->_ai->playerDead())
		return;

	switch (g_hdb->getGameState()) {
	case GAME_PLAY:
		if (g_hdb->getDebug() == 2)
			g_hdb->moveMap(x, y);
		break;
	case GAME_MENU:
		g_hdb->_menu->processInput(x, y);
		break;
	default:
		break;
	}
}

void Input::updateMouse(int newX, int newY) {
	_mouseX = CLIP(newX, 0, g_hdb->_screenWidth - 1);
	_mouseY = CLIP(newY, 0, g_hdb->_screenHeight - 1);

	// Turn Cursor back on?
	if (!g_hdb->_gfx->getPointer())
		g_hdb->_gfx->showPointer(true);

	// Check if LButton is being dragged
	if (_mouseLButton)
		stylusMove(_mouseX, _mouseY);
}

void Input::updateMouseButtons(int l, int m, int r) {
	_mouseLButton += l;
	_mouseMButton += m;
	_mouseRButton += r;

	// Check if LButton has been pressed
	// Check if LButton has been lifted
	if (_mouseLButton) {
		if (g_hdb->isPPC()) {
			stylusDown(_mouseX, _mouseY);
			return;
		}
		if (_mouseX > (g_hdb->_screenWidth - 32 * 5) && _mouseY < 240) {
			g_hdb->_window->checkInvSelect(_mouseX, _mouseY);
		} else if (_mouseX > (g_hdb->_screenWidth - 32 * 5) && _mouseY >= 240) {
			g_hdb->_window->checkDlvSelect(_mouseX, _mouseY);
		} else {
			if (g_hdb->getPause() && g_hdb->getGameState() == GAME_PLAY) {
				g_hdb->_window->checkPause(_mouseX, _mouseY);
				return;
			}
			stylusDown(_mouseX, _mouseY);
		}
	} else if (!_mouseLButton) {
		stylusUp(_mouseX, _mouseY);
	}

	// Check if MButton has been pressed
	if (_mouseMButton) {
		if (g_hdb->getPause() && g_hdb->getGameState() == GAME_PLAY)
			return;

		g_hdb->_ai->clearWaypoints();
		g_hdb->_sound->playSound(SND_POP);
	}

	// Check if RButton has been pressed
	if (_mouseRButton) {
		if (g_hdb->getPause() && g_hdb->getGameState() == GAME_PLAY)
			return;

		uint16 buttons = getButtons() | kButtonB;
		setButtons(buttons);
	} else if (!_mouseRButton) {
		uint16 buttons = getButtons() & ~kButtonB;
		setButtons(buttons);
	}
}

void Input::updateKeys(Common::Event event, bool keyDown) {
	static bool current = false, last = false;

	if (keyDown && event.kbd.keycode == _keyQuit) {
		g_hdb->quitGame();
		return;
	}

	uint16 buttons = getButtons();

	// PAUSE key pressed?
	last = current;
	if (keyDown && event.kbd.keycode == Common::KEYCODE_p && g_hdb->getGameState() == GAME_PLAY) {
		current = true;
		if (!last) {
			g_hdb->togglePause();
			g_hdb->_sound->playSound(SND_POP);
		}
	} else
		current = false;

	if (!g_hdb->getPause()) {
		if (event.kbd.keycode == _keyUp) {
			if (keyDown) {
				buttons |= kButtonUp;
				if (g_hdb->_gfx->getPointer())
					g_hdb->_gfx->showPointer(false);
			} else {
				buttons &= ~kButtonUp;
			}
		} else if (event.kbd.keycode == _keyDown) {
			if (keyDown) {
				buttons |= kButtonDown;
				if (g_hdb->_gfx->getPointer())
					g_hdb->_gfx->showPointer(false);
			} else {
				buttons &= ~kButtonDown;
			}
		} else if (event.kbd.keycode == _keyLeft) {
			if (keyDown) {
				buttons |= kButtonLeft;
				if (g_hdb->_gfx->getPointer())
					g_hdb->_gfx->showPointer(false);
			} else {
				buttons &= ~kButtonLeft;
			}
		} else if (event.kbd.keycode == _keyRight) {
			if (keyDown) {
				buttons |= kButtonRight;
				if (g_hdb->_gfx->getPointer())
					g_hdb->_gfx->showPointer(false);
			} else {
				buttons &= ~kButtonRight;
			}
		} else if (event.kbd.keycode == _keyUse) {
			if (keyDown) {
				buttons |= kButtonB;
				if (g_hdb->_gfx->getPointer())
					g_hdb->_gfx->showPointer(false);
			} else {
				buttons &= ~kButtonB;
			}
		}
	}

	if (event.kbd.keycode == _keyMenu) {
		if (keyDown) {
			buttons |= kButtonA;
			g_hdb->_gfx->showPointer(true);
			g_hdb->_menu->setMenuKey(1);
		} else {
			buttons &= ~kButtonA;
			g_hdb->_menu->setMenuKey(0);
		}
	} else if (event.kbd.keycode == _keyDebug) {
		if (keyDown)
			buttons |= kButtonExit;
		else
			buttons &= ~kButtonExit;
	}

	setButtons(buttons);
}

} // End of Namespace