/* 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.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-12-0/engines/parallaction/input.cpp $
 * $Id: input.cpp 33003 2008-07-11 13:06:28Z peres001 $
 *
 */

#include "common/events.h"

#include "parallaction/input.h"
#include "parallaction/parallaction.h"
#include "parallaction/debug.h"

namespace Parallaction {

// FIXME: the engine has 3 event loops. The following routine hosts the main one,
// and it's called from 8 different places in the code. There exist 2 more specialised
// loops which could possibly be merged into this one with some effort in changing
// caller code, i.e. adding condition checks.
//
uint16 Input::readInput() {

	Common::Event e;
	uint16 KeyDown = 0;

	_mouseButtons = kMouseNone;

	Common::EventManager *eventMan = _vm->_system->getEventManager();
	while (eventMan->pollEvent(e)) {

		switch (e.type) {
		case Common::EVENT_KEYDOWN:
			if (e.kbd.flags == Common::KBD_CTRL && e.kbd.keycode == 'd')
				_vm->_debugger->attach();
			if (_vm->getFeatures() & GF_DEMO) break;
			if (e.kbd.keycode == Common::KEYCODE_l) KeyDown = kEvLoadGame;
			if (e.kbd.keycode == Common::KEYCODE_s) KeyDown = kEvSaveGame;
			break;

		case Common::EVENT_LBUTTONDOWN:
			_mouseButtons = kMouseLeftDown;
			_mousePos = e.mouse;
			break;

		case Common::EVENT_LBUTTONUP:
			_mouseButtons = kMouseLeftUp;
			_mousePos = e.mouse;
			break;

		case Common::EVENT_RBUTTONDOWN:
			_mouseButtons = kMouseRightDown;
			_mousePos = e.mouse;
			break;

		case Common::EVENT_RBUTTONUP:
			_mouseButtons = kMouseRightUp;
			_mousePos = e.mouse;
			break;

		case Common::EVENT_MOUSEMOVE:
			_mousePos = e.mouse;
			break;

		case Common::EVENT_QUIT:
			_engineFlags |= kEngineQuit;
			return KeyDown;

		default:
			break;

		}

	}

	if (_vm->_debugger->isAttached())
		_vm->_debugger->onFrame();

	return KeyDown;

}

// FIXME: see comment for readInput()
void Input::waitForButtonEvent(uint32 buttonEventMask, int32 timeout) {

	if (buttonEventMask == kMouseNone) {
		_mouseButtons = kMouseNone;	// don't wait on nothing
		return;
	}

	const int32 LOOP_RESOLUTION = 30;
	if (timeout <= 0) {
		do {
			readInput();
			_vm->_system->delayMillis(LOOP_RESOLUTION);
		} while ((_mouseButtons & buttonEventMask) == 0);
	} else {
		do {
			readInput();
			_vm->_system->delayMillis(LOOP_RESOLUTION);
			timeout -= LOOP_RESOLUTION;
		} while ((timeout > 0) && (_mouseButtons & buttonEventMask) == 0);
	}

}

// FIXME: see comment for readInput()
void Input::waitUntilLeftClick() {

	do {
		readInput();
		_vm->_gfx->updateScreen();
		_vm->_system->delayMillis(30);
	} while (_mouseButtons != kMouseLeftUp);

	return;
}


void Input::updateGameInput() {

	int16 keyDown = readInput();

	debugC(3, kDebugInput, "translateInput: input flags (%i, %i, %i, %i)",
		!_mouseHidden,
		(_engineFlags & kEngineBlockInput) == 0,
		(_engineFlags & kEngineWalking) == 0,
		(_engineFlags & kEngineChangeLocation) == 0
	);

	if ((_mouseHidden) ||
		(_engineFlags & kEngineBlockInput) ||
		(_engineFlags & kEngineWalking) ||
		(_engineFlags & kEngineChangeLocation)) {

		return;
	}

	if (keyDown == kEvQuitGame) {
		_inputData._event = kEvQuitGame;
	} else
	if (keyDown == kEvSaveGame) {
		_inputData._event = kEvSaveGame;
	} else
	if (keyDown == kEvLoadGame) {
		_inputData._event = kEvLoadGame;
	} else {
		_inputData._mousePos = _mousePos;
		_inputData._event = kEvNone;
		if (!translateGameInput()) {
			translateInventoryInput();
		}
	}

}

void Input::updateCommentInput() {
	waitUntilLeftClick();

	_vm->hideDialogueStuff();
	_vm->_gfx->setHalfbriteMode(false);

	_inputMode = kInputModeGame;
}

InputData* Input::updateInput() {

	_inputData._event = kEvNone;

	switch (_inputMode) {
	case kInputModeComment:
		updateCommentInput();
		break;

	case kInputModeGame:
		updateGameInput();
		break;
	}

	return &_inputData;
}

bool Input::translateGameInput() {

	if ((_engineFlags & kEnginePauseJobs) || (_engineFlags & kEngineInventory)) {
		return false;
	}

	if (_actionAfterWalk) {
		// if walking is over, then take programmed action
		_inputData._event = kEvAction;
		_actionAfterWalk = false;
		return true;
	}

	if (_mouseButtons == kMouseRightDown) {
		// right button down shows inventory

		if (_vm->hitZone(kZoneYou, _mousePos.x, _mousePos.y) && (_activeItem._id != 0)) {
			_activeItem._index = (_activeItem._id >> 16) & 0xFFFF;
			_engineFlags |= kEngineDragging;
		}

		_inputData._event = kEvOpenInventory;
		_transCurrentHoverItem = -1;
		return true;
	}

	// test if mouse is hovering on an interactive zone for the currently selected inventory item
	ZonePtr z = _vm->hitZone(_activeItem._id, _mousePos.x, _mousePos.y);

	if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((_engineFlags & kEngineWalking) == 0)) && ((!z) || ((z->_type & 0xFFFF) != kZoneCommand))) {
		_inputData._event = kEvWalk;
		return true;
	}

	if ((z != _hoverZone) && (_hoverZone)) {
		_hoverZone = nullZonePtr;
		_inputData._event = kEvExitZone;
		return true;
	}

	if (!z) {
		_inputData._event = kEvNone;
		return true;
	}

	if ((!_hoverZone) && ((z->_flags & kFlagsNoName) == 0)) {
		_hoverZone = z;
		_inputData._event = kEvEnterZone;
		_inputData._label = z->_label;
		return true;
	}

	if ((_mouseButtons == kMouseLeftUp) && ((_activeItem._id != 0) || ((z->_type & 0xFFFF) == kZoneCommand))) {

		_inputData._zone = z;
		if (z->_flags & kFlagsNoWalk) {
			// character doesn't need to walk to take specified action
			_inputData._event = kEvAction;

		} else {
			// action delayed: if Zone defined a moveto position the character is programmed to move there,
			// else it will move to the mouse position
			_inputData._event = kEvWalk;
			_actionAfterWalk = true;
			if (z->_moveTo.y != 0) {
				_inputData._mousePos = z->_moveTo;
			}
		}

		_vm->beep();
		_vm->setArrowCursor();
		return true;
	}

	return true;

}

bool Input::translateInventoryInput() {

	if ((_engineFlags & kEngineInventory) == 0) {
		return false;
	}

	// in inventory
	int16 _si = _vm->getHoverInventoryItem(_mousePos.x, _mousePos.y);

	if (_mouseButtons == kMouseRightUp) {
		// right up hides inventory

		_inputData._event = kEvCloseInventory;
		_inputData._inventoryIndex = _vm->getHoverInventoryItem(_mousePos.x, _mousePos.y);
		_vm->highlightInventoryItem(-1);			// disable

		if ((_engineFlags & kEngineDragging) == 0) {
			return true;
		}

		_engineFlags &= ~kEngineDragging;
		ZonePtr z = _vm->hitZone(kZoneMerge, _activeItem._index, _vm->getInventoryItemIndex(_inputData._inventoryIndex));

		if (z) {
			_vm->dropItem(z->u.merge->_obj1);
			_vm->dropItem(z->u.merge->_obj2);
			_vm->addInventoryItem(z->u.merge->_obj3);
			_vm->_cmdExec->run(z->_commands);
		}

		return true;
	}

	if (_si == _transCurrentHoverItem) {
		_inputData._event = kEvNone;
		return true;
	}

	_transCurrentHoverItem = _si;
	_inputData._event = kEvHoverInventory;
	_inputData._inventoryIndex = _si;
	return true;

}

void Input::showCursor(bool visible) {
	_mouseHidden = !visible;
	_vm->_system->showMouse(visible);
}


} // namespace Parallaction
