/* 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 "lastexpress/data/scene.h"

#include "lastexpress/data/background.h"

#include "lastexpress/helpers.h"
#include "lastexpress/lastexpress.h"
#include "lastexpress/resource.h"

#include "common/textconsole.h"
#include "common/stream.h"

namespace LastExpress {

SceneHotspot::~SceneHotspot() {
	for (uint i = 0; i < _coords.size(); i++)
		SAFE_DELETE(_coords[i]);

	_coords.clear();
}

SceneHotspot *SceneHotspot::load(Common::SeekableReadStream *stream) {
	SceneHotspot *hs = new SceneHotspot();

	// Rect
	hs->rect.left = (int16)stream->readUint16LE();
	hs->rect.right = (int16)stream->readUint16LE();
	hs->rect.top = (int16)stream->readUint16LE();
	hs->rect.bottom = (int16)stream->readUint16LE();

	hs->coordsOffset = stream->readUint32LE();
	hs->scene = (SceneIndex)stream->readUint16LE();
	hs->location = stream->readByte();
	hs->action = (Action)stream->readByte();
	hs->param1 = stream->readByte();
	hs->param2 = stream->readByte();
	hs->param3 = stream->readByte();
	hs->cursor = stream->readByte();
	hs->next = stream->readUint32LE();

	debugC(10, kLastExpressDebugScenes, "\thotspot: scene=%d location=%02d action=%d param1=%02d param2=%02d param3=%02d cursor=%02d rect=(%d, %d)x(%d, %d)",
	                                   hs->scene, hs->location, hs->action, hs->param1, hs->param2, hs->param3, hs->cursor, hs->rect.left, hs->rect.top, hs->rect.right, hs->rect.bottom);
	debugC(10, kLastExpressDebugScenes, "\t         coords=%d next=%d ", hs->coordsOffset, hs->next);

	// Read all coords data
	uint32 offset = hs->coordsOffset;
	while (offset != 0) {

		SceneCoord *sceneCoord = new SceneCoord;

		stream->seek(offset, SEEK_SET);

		sceneCoord->field_0 = stream->readSint32LE();
		sceneCoord->field_4 = stream->readSint32LE();
		sceneCoord->field_8 = stream->readByte();
		sceneCoord->next = stream->readUint32LE();

		hs->_coords.push_back(sceneCoord);

		offset = sceneCoord->next;
	}

	return hs;
}

Common::String SceneHotspot::toString() const {
	Common::String output = "";

	output += Common::String::format("    hotspot: scene=%d location=%02d action=%d param1=%02d param2=%02d param3=%02d cursor=%02d rect=(%d, %d)x(%d, %d)",
	                                   scene, location, action, param1, param2, param3, cursor, rect.left, rect.top, rect.right, rect.bottom);

	return output;
}

bool SceneHotspot::isInside(const Common::Point &point) {

	bool contains = rect.contains(point);

	if (_coords.empty() || !contains)
		return contains;

	// Checks extended coordinates
	for (uint i = 0; i < _coords.size(); i++) {

		SceneCoord *sCoord = _coords[i];

		bool cont;
		if (sCoord->field_8)
			cont = (sCoord->field_4 + point.x * sCoord->field_0 + 1000 * point.y) >= 0;
		else
			cont = (sCoord->field_4 + point.x * sCoord->field_0 + 1000 * point.y) <= 0;

		if (!cont)
			return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////
// Scene
Scene::~Scene() {
	// Free the hotspots
	for (int i = 0; i < (int)_hotspots.size(); i++)
		delete _hotspots[i];
}

Scene *Scene::load(Common::SeekableReadStream *stream) {
	Scene *scene = new Scene();

	stream->read(&scene->_name, sizeof(scene->_name));
	scene->_sig = stream->readByte();
	scene->entityPosition = (EntityPosition)stream->readUint16LE();
	scene->location = (Location)stream->readUint16LE();
	scene->car = (CarIndex)stream->readUint16LE();
	scene->position = stream->readByte();
	scene->type = (Type)stream->readByte();
	scene->param1 = stream->readByte();
	scene->param2 = stream->readByte();
	scene->param3 = stream->readByte();
	scene->_hotspot = stream->readUint32LE();

	return scene;
}

void Scene::loadHotspots(Common::SeekableReadStream *stream) {
	if (!_hotspots.empty())
		return;

	debugC(10, kLastExpressDebugScenes, "Scene:  name=%s, sig=%02d, entityPosition=%d, location=%d", _name, _sig, entityPosition, location);
	debugC(10, kLastExpressDebugScenes, "\tcar=%02d, position=%02d, type=%02d, param1=%02d", car, position, type, param1);
	debugC(10, kLastExpressDebugScenes, "\tparam2=%02d, param3=%02d, hotspot=%d\n", param2, param3, _hotspot);

	// Read all hotspots
	if (_hotspot != 0) {
		stream->seek((int32)_hotspot, SEEK_SET);
		SceneHotspot *hotspot = SceneHotspot::load(stream);
		while (hotspot) {
			_hotspots.push_back(hotspot);

			if (hotspot->next == 0)
				break;

			stream->seek((int32)hotspot->next, SEEK_SET);
			hotspot = SceneHotspot::load(stream);
		}
	}
}

bool Scene::checkHotSpot(const Common::Point &coord, SceneHotspot **hotspot) {
	bool found = false;
	int _location = 0;

	for (int i = 0; i < (int)_hotspots.size(); i++) {
		if (_hotspots[i]->isInside(coord)) {
			if (_location <= _hotspots[i]->location) {
				_location = _hotspots[i]->location;
				*hotspot = _hotspots[i];
				found = true;
			}
		}
	}

	return found;
}

SceneHotspot *Scene::getHotspot(uint index) {
	if (_hotspots.empty())
		error("[Scene::getHotspot] Scene does not have any hotspots");

	if (index >= _hotspots.size())
		error("[Scene::getHotspot] Invalid index (was: %d, max: %d)", index, _hotspots.size() - 1);

	return _hotspots[index];
}

Common::Rect Scene::draw(Graphics::Surface *surface) {
	// Safety checks
	Common::Rect rect;

	Common::String sceneName(_name);
	sceneName.trim();
	if (sceneName.empty())
		error("[Scene::draw] This scene is not a valid drawing scene");

	// Load background
	Background *background = ((LastExpressEngine *)g_engine)->getResourceManager()->loadBackground(sceneName);
	if (background) {
		rect = background->draw(surface);
		delete background;
	}

	return rect;
}

Common::String Scene::toString() {
	Common::String output = "";

	output += Common::String::format("Scene:  name=%s, sig=%02d, entityPosition=%d, location=%d\n", _name, _sig, entityPosition, location);
	output += Common::String::format("        car=%02d, position=%02d, type=%02d, param1=%02d\n", car, position, type, param1);
	output += Common::String::format("        param2=%02d, param3=%02d, hotspot=%d\n", param2, param3, _hotspot);

	// Hotspots
	if (_hotspots.size() != 0) {
		output += "\nHotspots:\n";
		for (int i = 0; i < (int)_hotspots.size(); i++)
			output += _hotspots[i]->toString() + "\n";
	}

	return output;
}

//////////////////////////////////////////////////////////////////////////
// SceneLoader
SceneLoader::SceneLoader() : _stream(NULL) {}

SceneLoader::~SceneLoader() {
	clear();
}

void SceneLoader::clear() {
	// Remove all scenes
	for (int i = 0; i < (int)_scenes.size(); i++)
		delete _scenes[i];

	_scenes.clear();

	delete _stream;
}

bool SceneLoader::load(Common::SeekableReadStream *stream) {
	if (!stream)
		return false;

	clear();

	_stream = stream;

	// Read the default scene to get the total number of scenes
	Scene *header = Scene::load(_stream);
	if (!header)
		error("[SceneLoader::load] Invalid data file");

	debugC(2, kLastExpressDebugScenes, "   found %d entries", header->entityPosition); /* Header entityPosition is the scene count */

	if (header->entityPosition > 2500) {
		delete header;

		return false;
	}

	_scenes.push_back(header);

	// Read all the chunks
	for (uint i = 0; i < (uint)header->entityPosition; ++i) {
		Scene *scene = Scene::load(_stream);
		if (!scene)
			break;

		_scenes.push_back(scene);
	}

	return true;
}

Scene *SceneLoader::get(SceneIndex index) {
	if (_scenes.empty())
		return NULL;

	if (index > _scenes.size())
		return NULL;

	// Load the hotspots if needed
	_scenes[(int)index]->loadHotspots(_stream);

	return _scenes[(int)index];
}

} // End of namespace LastExpress
