/* 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/>.
 *
 */

#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/bbdou_credits.h"
#include "illusions/actor.h"
#include "illusions/dictionary.h"
#include "illusions/textdrawer.h"
#include "illusions/time.h"
#include "illusions/resources/scriptresource.h"

namespace Illusions {

BbdouCredits::BbdouCredits(IllusionsEngine_BBDOU *vm)
	: _vm(vm) {
}

BbdouCredits::~BbdouCredits() {
}

void BbdouCredits::start(uint32 endSignalPropertyId, float speedModifier) {
	_endSignalPropertyId = endSignalPropertyId;
	_currFontId = 0x120004;
	_currLineIndex = 1;
	_split = false;
	// convertTextData();
	initCreditsItems();
	createCreditsThread(speedModifier);
}

void BbdouCredits::stop() {
	freeCreditsItems();
}

void BbdouCredits::drawNextLine() {
	uint leftIndex, rightIndex;

	if (!readNextLine(leftIndex, rightIndex)) {
		_vm->_scriptResource->_properties.set(_endSignalPropertyId, true);
		return;
	}

	if (leftIndex) {
		const char *leftText = getText(leftIndex);
		if (leftText && strlen(leftText) != 0) {
			uint32 objectId = getNextFreeObjectId();
			int alignment = rightIndex ? 1 : 2;
			drawTextToControl(objectId, leftText, alignment);
		}
	}

	if (rightIndex) {
		const char *rightText = getText(rightIndex);
		if (rightText && strlen(rightText) != 0) {
			uint32 objectId = getNextFreeObjectId();
			drawTextToControl(objectId, rightText, 4);
		}
	}

}

void charToWChar(const char *text, uint16 *wtext, uint size) {
	while (*text != 0 && size > 1) {
		*wtext++ = (byte)*text++;
		/*
		byte c = (byte)*text++;
		if (c > 127) c = 32;
		*wtext = c;
		debug("%04X", *wtext);
		++wtext;
		*/
		--size;
	}
	*wtext++ = 0;
}

void BbdouCredits::drawTextToControl(uint32 objectId, const char *text, uint alignment) {
	uint16 wtext[128];
	charToWChar(text, wtext, ARRAYSIZE(wtext));

	Control *control = _vm->getObjectControl(objectId);
	FontResource *font = _vm->_dict->findFont(_currFontId);
	TextDrawer textDrawer;
	WidthHeight dimensions;
	uint16 *outText;
	control->getActorFrameDimensions(dimensions);
	control->fillActor(0);
	textDrawer.wrapText(font, wtext, &dimensions, Common::Point(0, 0), alignment, outText);
	textDrawer.drawText(_vm->_screen, control->_actor->_surface, 0, 0);
	control->_actor->_flags |= Illusions::ACTOR_FLAG_4000;

}

bool BbdouCredits::readNextLine(uint &leftIndex, uint &rightIndex) {
	bool done = false;
	int textLines = 0;
	leftIndex = 0;
	rightIndex = 0;

	do {
		uint lineIndex = _currLineIndex++;
		const char *text = getText(lineIndex);
		if (text[0] == '@') {
			const char *command = text + 1;
			if (!strcmp(command, "end"))
				done = true;
			if (!strcmp(command, "bold"))
				_currFontId = 0x120005;
			else if (!strcmp(command, "normal"))
				_currFontId = 0x120004;
			else if (!strcmp(command, "center"))
				_split = false;
			else if (!strcmp(command, "split"))
				_split = true;
			else
				done = true;
		} else {
			++textLines;
			if (!_split) {
				leftIndex = lineIndex;
				done = true;
			} else if (textLines > 1) {
				rightIndex = lineIndex;
				done = true;
			} else {
				leftIndex = lineIndex;
			}
		}
	} while (!done);

	return textLines > 0;
}

void BbdouCredits::initCreditsItems() {
	for (uint i = 0; i < kCreditsItemsCount; ++i) {
		uint32 objectId = _vm->_controls->newTempObjectId();
		_vm->_controls->placeActor(0x00050188, Common::Point(320, 480), 0x00060BE1, objectId, 0);
		Control *control = _vm->_dict->getObjectControl(objectId);
		control->startSequenceActor(0x60BE2, 2, 0);
		_items[i].isUsed = false;
		_items[i].objectId = objectId;
	}
}

void BbdouCredits::freeCreditsItems() {
	for (uint i = 0; i < kCreditsItemsCount; ++i) {
		Control *control = _vm->_dict->getObjectControl(_items[i].objectId);
		control->disappearActor();
	}
}

uint32 BbdouCredits::getNextFreeObjectId() {
	for (uint i = 0; i < kCreditsItemsCount; ++i) {
		if (!_items[i].isUsed) {
			_items[i].isUsed = true;
			return _items[i].objectId;
		}
	}
	return 0;
}

void BbdouCredits::removeText(uint32 objectId) {
	for (uint i = 0; i < kCreditsItemsCount; ++i) {
		if (_items[i].objectId == objectId) {
			_items[i].isUsed = false;
			resetObjectPos(objectId);
		}
	}
}

void BbdouCredits::resetObjectPos(uint32 objectId) {
	Control *control = _vm->_dict->getObjectControl(objectId);
	control->setActorPosition(Common::Point(320, 480));
}

void BbdouCredits::createCreditsThread(float speedModifier) {
	uint32 tempThreadId = _vm->newTempThreadId();
	CreditsThread *creditsThread = new CreditsThread(_vm, this, tempThreadId, speedModifier);
	_vm->_threads->startThread(creditsThread);
}

void BbdouCredits::updateTexts(int yIncr) {
	for (uint i = 0; i < kCreditsItemsCount; ++i) {
		if (_items[i].isUsed) {
			Control *control = _vm->_dict->getObjectControl(_items[i].objectId);
			Common::Point pt = control->getActorPosition();
			pt.y += yIncr;
			control->setActorPosition(pt);
			if (pt.y <= 0)
				removeText(_items[i].objectId);
		}
	}
}

// CreditsThread

CreditsThread::CreditsThread(IllusionsEngine_BBDOU *vm, BbdouCredits *credits, uint32 threadId, float speedModifier)
	: Thread(vm, threadId, 0, 0), _speedModifier(speedModifier), _lastFraction(0.0), _credits(credits) {
	_type = kTTSpecialThread;
	_lastUpdateTime = getCurrentTime();
}

int CreditsThread::onUpdate() {
	uint32 currTime = getCurrentTime();
	float fltDelta = (currTime - _lastUpdateTime) * _speedModifier + _lastFraction;
	int delta = (int)fltDelta;
	_lastFraction = fltDelta - delta;
	if (delta != 0)
		_credits->updateTexts(-delta);
	_lastUpdateTime = currTime;
	return 2;
}

void CreditsThread::onNotify() {
	_lastUpdateTime = getCurrentTime();
}

void CreditsThread::onResume() {
	onNotify();
}

void CreditsThread::onTerminated() {
	_credits->stop();
}

} // End of namespace Illusions
