/* 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

// Object map / Object click-area module

// Polygon Hit Test code ( HitTestPoly() ) adapted from code (C) Eric Haines
// appearing in Graphics Gems IV, "Point in Polygon Strategies."
// p. 24-46, code: p. 34-45

#include "saga/saga.h"

#include "saga/gfx.h"
#include "saga/console.h"
#include "saga/font.h"
#include "saga/interface.h"
#include "saga/objectmap.h"
#include "saga/actor.h"
#include "saga/scene.h"
#include "saga/isomap.h"
#ifdef SAGA_DEBUG
#include "saga/render.h"
#endif

namespace Saga {

void HitZone::load(SagaEngine *vm, Common::MemoryReadStreamEndian *readStream, int index, int sceneNumber) {
	_index = index;
	_flags = readStream->readByte();
	_clickAreas.resize(readStream->readByte());
	_rightButtonVerb = readStream->readByte();
	readStream->readByte(); // pad
	_nameIndex = readStream->readUint16();
	_scriptNumber = readStream->readUint16();

	for (ClickAreas::iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
		i->resize(readStream->readUint16LE());

		assert(!i->empty());

		for (ClickArea::iterator j = i->begin(); j != i->end(); ++j) {
			j->x = readStream->readSint16();
			j->y = readStream->readSint16();

			// WORKAROUND: bug #2154: "ITE: Riff ignores command in Ferret merchant center"
			// Apparently ITE Mac version has bug in game data. Both ObjectMap and ActionMap
			// for exit area are little taller (y = 123) and thus Riff goes to exit
			// when clicked on barrel of nails.
			if (vm->getGameId() == GID_ITE) {
				if (sceneNumber == 18 && index == 0 && (i == _clickAreas.begin()) && (j == i->begin()) && j->y == 123) {
					j->y = 129;
				}
			}
		}
	}
}

bool HitZone::getSpecialPoint(Point &specialPoint) const {
	for (ClickAreas::const_iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
		if (i->size() == 1) {
			specialPoint = (*i)[0];
			return true;
		}
	}
	return false;
}

bool HitZone::hitTest(const Point &testPoint) {
	const Point *points;
	uint pointsCount;

	if (_flags & kHitZoneEnabled) {
		for (ClickAreas::const_iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
			pointsCount = i->size();
			if (pointsCount < 2) {
				continue;
			}
			points = &i->front();
			if (pointsCount == 2) {
				// Hit-test a box region
				if ((testPoint.x >= points[0].x) &&
					(testPoint.x <= points[1].x) &&
					(testPoint.y >= points[0].y) &&
					(testPoint.y <= points[1].y)) {
						return true;
					}
			} else {
				// Hit-test a polygon
				if (hitTestPoly(points, pointsCount, testPoint)) {
					return true;
				}
			}
		}
	}
	return false;
}

#ifdef SAGA_DEBUG
void HitZone::draw(SagaEngine *vm, int color) {
	int pointsCount, j;
	Location location;
	HitZone::ClickArea tmpPoints;
	const Point *points;
	Point specialPoint1;
	Point specialPoint2;

	for (ClickAreas::const_iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
		pointsCount = i->size();
		points = &i->front();
		if (vm->_scene->getFlags() & kSceneFlagISO) {
			tmpPoints.resize(pointsCount);
			for (j = 0; j < pointsCount; j++) {
				location.u() = points[j].x;
				location.v() = points[j].y;
				location.z = 0;
				vm->_isoMap->tileCoordsToScreenPoint(location, tmpPoints[j]);
			}
			points = &tmpPoints.front();
		}

		if (pointsCount == 2) {
			// 2 points represent a box
			vm->_gfx->drawFrame(points[0], points[1], color);
		} else {
			if (pointsCount > 2) {
				// Otherwise draw a polyline
				// Do a full refresh so that the polyline can be shown
				vm->_render->setFullRefresh(true);
				vm->_gfx->drawPolyLine(points, pointsCount, color);
			}
		}
	}
	if (getSpecialPoint(specialPoint1)) {
		specialPoint2 = specialPoint1;
		specialPoint1.x--;
		specialPoint1.y--;
		specialPoint2.x++;
		specialPoint2.y++;
		vm->_gfx->drawFrame(specialPoint1, specialPoint2, color);
	}
}
#endif

// Loads an object map resource ( objects ( clickareas ( points ) ) )
void ObjectMap::load(const ByteArray &resourceData) {

	if (!_hitZoneList.empty()) {
		error("ObjectMap::load _hitZoneList not empty");
	}

	if (resourceData.empty()) {
		return;
	}

	if (resourceData.size() < 4) {
		error("ObjectMap::load wrong resourceLength");
	}

	ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());

	_hitZoneList.resize(readS.readUint16());

	int idx = 0;
	for (HitZoneArray::iterator i = _hitZoneList.begin(); i != _hitZoneList.end(); ++i) {
		i->load(_vm, &readS, idx++, _vm->_scene->currentSceneNumber());
	}
}

void ObjectMap::clear() {
	_hitZoneList.clear();
}

#ifdef SAGA_DEBUG
void ObjectMap::draw(const Point& testPoint, int color, int color2) {
	int hitZoneIndex;
	Common::String txtBuf;
	Point pickPoint;
	Point textPoint;
	Location pickLocation;
	pickPoint = testPoint;
	if (_vm->_scene->getFlags() & kSceneFlagISO) {
		assert(_vm->_actor->_protagonist);
		pickPoint.y -= _vm->_actor->_protagonist->_location.z;
		_vm->_isoMap->screenPointToTileCoords(pickPoint, pickLocation);
		pickLocation.toScreenPointUV(pickPoint);
	}

	hitZoneIndex = hitTest(pickPoint);

	for (HitZoneArray::iterator i = _hitZoneList.begin(); i != _hitZoneList.end(); ++i) {
		i->draw(_vm, (hitZoneIndex == i->getIndex()) ? color2 : color);
	}

	if (hitZoneIndex != -1) {
		txtBuf = Common::String::format("hitZone %d", hitZoneIndex);
		textPoint.x = 2;
		textPoint.y = 2;
		_vm->_font->textDraw(kKnownFontSmall, txtBuf.c_str(), textPoint, kITEColorBrightWhite, kITEColorBlack, kFontOutline);
	}
}
#endif

int ObjectMap::hitTest(const Point& testPoint) {

	// Loop through all scene objects
	for (HitZoneArray::iterator i = _hitZoneList.begin(); i != _hitZoneList.end(); ++i) {
		if (i->hitTest(testPoint)) {
			return i->getIndex();
		}
	}

	return -1;
}

void ObjectMap::cmdInfo() {
	_vm->_console->debugPrintf("%d zone(s) loaded.\n\n", _hitZoneList.size());
}

} // End of namespace Saga
