/* 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/illusions.h"
#include "illusions/actor.h"
#include "illusions/camera.h"
#include "illusions/cursor.h"
#include "illusions/dictionary.h"
#include "illusions/fixedpoint.h"
#include "illusions/input.h"
#include "illusions/screen.h"
#include "illusions/scriptopcodes.h"
#include "illusions/sequenceopcodes.h"
#include "illusions/thread.h"
#include "illusions/threads/talkthread.h"

namespace Illusions {

// DefaultSequences

uint32 DefaultSequences::use(uint32 sequenceId) {
	ItemsIterator it = Common::find_if(_items.begin(), _items.end(), DefaultSequenceEqual(sequenceId));
	return it != _items.end() ? (*it)._newSequenceId : sequenceId;
}

void DefaultSequences::set(uint32 sequenceId, uint32 newSequenceId) {
	ItemsIterator it = Common::find_if(_items.begin(), _items.end(), DefaultSequenceEqual(sequenceId));
	if (it == _items.end())
		_items.push_back(DefaultSequence(sequenceId, newSequenceId));
	else if (sequenceId == newSequenceId)
		_items.remove_at(it - _items.begin());
	else
		(*it)._newSequenceId = newSequenceId;
}

// Actor

Actor::Actor(IllusionsEngine *vm)
	: _vm(vm), _pauseCtr(0) {
	_pauseCtr = 0;
	_spriteFlags = 0;
	_drawFlags = 0;
	_flags = 0;
	_scale = 100;
	_frameIndex = 0;
	_newFrameIndex = 0;
	_surfInfo._pixelSize = 0;
	_surfInfo._dimensions._width = 0;
	_surfInfo._dimensions._height = 0;
	_surface = nullptr;
	_frames = nullptr;
	_scaleLayer = nullptr;
	_priorityLayer = nullptr;
	_regionLayer = nullptr;
	_pathWalkPoints = nullptr;
	_pathWalkRects = nullptr;
	_position.x = 0;
	_position.y = 0;
	_position2.x = 0;
	_position2.y = 0;
	_facing = 64;
	_regionIndex = 0;
	_fontId = 0;
	_actorIndex = 0;
	_parentObjectId = 0;
	_linkIndex = 0;
	_linkIndex2 = 0;
	for (uint i = 0; i < kSubObjectsCount; ++i) {
		_subobjects[i] = 0;
	}
	_notifyThreadId1 = 0;
	_notifyThreadId2 = 0;
	_entryTblPtr = nullptr;
	_seqCodeIp = nullptr;
	_sequenceId = 0;
	_seqCodeValue1 = 0;
	_seqCodeValue2 = 600;
	_seqCodeValue3 = 0;

	_notifyId3C = 0;

	_controlRoutine = nullptr;
	setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, Controls>(_vm->_controls, &Controls::actorControlRoutine));

	_walkCallerThreadId1 = 0;
	_pathAngle = 0;
	_pathFlag50 = false;
	_pathCtrX = 0;
	_pathCtrY = 0;
	_pathInitialPosFlag = true;
	_pathInitialPos.x = 0;
	_pathInitialPos.y = 0;
	_pathPoints = 0;
	_pathPointIndex = 0;
	_pathPointsCount = 0;
	_pathNode = nullptr;

}

Actor::~Actor() {
	delete _controlRoutine;
}

void Actor::pause() {
	++_pauseCtr;
}

void Actor::unpause() {
	--_pauseCtr;
}

void Actor::createSurface(SurfInfo &surfInfo) {
	_surface = _vm->_screen->allocSurface(surfInfo);
	if (_vm->getGameId() == kGameIdDuckman) {
		if (_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) {
			if (_frameIndex) {
				_flags |= Illusions::ACTOR_FLAG_2000;
			}
			_flags |= Illusions::ACTOR_FLAG_4000;
		}
	} else {
		if (_frameIndex) {
			_flags |= Illusions::ACTOR_FLAG_2000;
			_flags |= Illusions::ACTOR_FLAG_4000;
		}
	}
}

void Actor::destroySurface() {
	if (_surface) {
		_surface->free();
		delete _surface;
		_surface = nullptr;
	}
}

void Actor::initSequenceStack() {
	_seqStackCount = 0;
}

void Actor::pushSequenceStack(int16 value) {
	_seqStack[_seqStackCount++] = value;
}

int16 Actor::popSequenceStack() {
	return _seqStack[--_seqStackCount];
}

void Actor::setControlRoutine(ActorControlRoutine *controlRoutine) {
	delete _controlRoutine;
	_controlRoutine = controlRoutine;
}

void Actor::runControlRoutine(Control *control, uint32 deltaTime) {
	if (_controlRoutine)
		(*_controlRoutine)(control, deltaTime);
}

bool Actor::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
	if (_namedPoints->findNamedPoint(namedPointId, pt)) {
		pt.x += _position.x;
		pt.y += _position.y;
		return true;
	}
	return false;
}

// Control

Control::Control(IllusionsEngine *vm)
	: _vm(vm) {
	_flags = 0;
	_pauseCtr = 0;
	_priority = 0;
	_objectId = 0;
	_bounds._topLeft.x = 0;
	_bounds._topLeft.y = 0;
	_bounds._bottomRight.x = 0;
	_bounds._bottomRight.y = 0;
	_feetPt.x = 0;
	_feetPt.y = 0;
	_position.x = 0;
	_position.y = 0;
	_actorTypeId = 0;
	_actor = nullptr;
	_sceneId = _vm->getCurrentScene();
}

Control::~Control() {
}

void Control::pause() {

	if (_vm->getGameId() == kGameIdBBDOU || !(_flags & 4)) {
		_vm->_dict->setObjectControl(_objectId, nullptr);
		if (_objectId == Illusions::CURSOR_OBJECT_ID)
			_vm->setCursorControl(nullptr);
	}

	if (_actor && !(_actor->_flags & Illusions::ACTOR_FLAG_200))
		_actor->destroySurface();

}

void Control::unpause() {

	if (_vm->getGameId() == kGameIdBBDOU || !(_flags & 4)) {
		_vm->_dict->setObjectControl(_objectId, this);
		if (_objectId == Illusions::CURSOR_OBJECT_ID)
			_vm->setCursorControl(this);
	}

	if (_actor && !(_actor->_flags & Illusions::ACTOR_FLAG_200)) {
		SurfInfo surfInfo;
		ActorType *actorType = _vm->_dict->findActorType(_actorTypeId);
		if (actorType)
			surfInfo = actorType->_surfInfo;
		else
			surfInfo = _actor->_surfInfo;
		_actor->createSurface(surfInfo);
	}
}

void Control::appearActor() {
	if (_vm->getGameId() == kGameIdDuckman) {
		_flags |= 1;
		_actor->_flags |= Illusions::ACTOR_FLAG_IS_VISIBLE;
		if (_objectId == Illusions::CURSOR_OBJECT_ID) {
			if (_actor->_frameIndex) {
				_actor->_flags |= Illusions::ACTOR_FLAG_2000;
				_actor->_flags |= Illusions::ACTOR_FLAG_4000;
			}
			_vm->_input->discardAllEvents();
		}
	} else {
		if (_objectId == Illusions::CURSOR_OBJECT_ID) {
			_vm->showCursor();
		} else {
			if (_actor->_frameIndex || _actorTypeId == 0x50004) {
				_actor->_flags |= Illusions::ACTOR_FLAG_IS_VISIBLE;
			} else {
				_actor->_flags |= Illusions::ACTOR_FLAG_1000;
			}
			for (uint i = 0; i < kSubObjectsCount; ++i) {
				if (_actor->_subobjects[i]) {
					Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
					subControl->appearActor();
				}
			}
		}
	}
}

void Control::disappearActor() {
	if (_vm->getGameId() == kGameIdDuckman) {
		_flags &= ~1;
		_actor->_flags &= ~Illusions::ACTOR_FLAG_IS_VISIBLE;
	} else {
		if (_objectId == Illusions::CURSOR_OBJECT_ID) {
			_vm->hideCursor();
		} else {
			_actor->_flags &= ~Illusions::ACTOR_FLAG_IS_VISIBLE;
			_actor->_flags &= ~Illusions::ACTOR_FLAG_1000;
			for (uint i = 0; i < kSubObjectsCount; ++i) {
				if (_actor->_subobjects[i]) {
					Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
					subControl->disappearActor();
				}
			}
		}
	}
}

bool Control::isActorVisible() {
	return (_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) != 0;
}

void Control::activateObject() {
	_flags |= 1;
	if (_actor) {
		for (uint i = 0; i < kSubObjectsCount; ++i) {
			if (_actor->_subobjects[i]) {
				Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
				subControl->activateObject();
			}
		}
	}
}

void Control::deactivateObject() {
	_flags &= ~1;
	if (_actor) {
		for (uint i = 0; i < kSubObjectsCount; ++i) {
			if (_actor->_subobjects[i]) {
				Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
				subControl->deactivateObject();
			}
		}
	}
}

void Control::readPointsConfig(byte *pointsConfig) {
	_bounds._topLeft.x = READ_LE_UINT16(pointsConfig + 0);
	_bounds._topLeft.y = READ_LE_UINT16(pointsConfig + 2);
	pointsConfig += 4;
	_bounds._bottomRight.x = READ_LE_UINT16(pointsConfig + 0);
	_bounds._bottomRight.y = READ_LE_UINT16(pointsConfig + 2);
	pointsConfig += 4;
	_feetPt.x = READ_LE_UINT16(pointsConfig + 0);
	_feetPt.y = READ_LE_UINT16(pointsConfig + 2);
	pointsConfig += 4;
	_position.x = READ_LE_UINT16(pointsConfig + 0);
	_position.y = READ_LE_UINT16(pointsConfig + 2);
	pointsConfig += 4;
	for (uint i = 0; i < kSubObjectsCount; ++i) {
		_subobjectsPos[i].x = READ_LE_UINT16(pointsConfig + 0);
		_subobjectsPos[i].y = READ_LE_UINT16(pointsConfig + 2);
		pointsConfig += 4;
	}
}

void Control::setActorPosition(Common::Point position) {
	_actor->_position = position;
}

Common::Point Control::getActorPosition() {
	if (_actor)
		return _actor->_position;
	return _position;
}

void Control::setActorScale(int scale) {
	_actor->_scale = scale;
	for (uint i = 0; i < kSubObjectsCount; ++i) {
		if (_actor->_subobjects[i]) {
			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
			subControl->setActorScale(scale);
		}
	}
}

void Control::faceActor(uint facing) {
	_actor->_facing = facing;
	for (uint i = 0; i < kSubObjectsCount; ++i) {
		if (_actor->_subobjects[i]) {
			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
			subControl->faceActor(facing);
		}
	}
}

void Control::linkToObject(uint32 parentObjectId, uint32 linkedObjectValue) {
	_actor->_parentObjectId = parentObjectId;
	_actor->_linkIndex = linkedObjectValue;
}

void Control::unlinkObject() {
	_actor->_parentObjectId = 0;
	_actor->_linkIndex = 0;
}

void Control::clearNotifyThreadId1() {
	_actor->_notifyThreadId1 = 0;
}

void Control::clearNotifyThreadId2() {
	for (uint i = 0; i < kSubObjectsCount; ++i) {
		if (_actor->_subobjects[i]) {
			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
			subControl->_actor->_flags &= ~Illusions::ACTOR_FLAG_80;
			subControl->_actor->_entryTblPtr = nullptr;
			subControl->_actor->_notifyThreadId2 = 0;
		}
	}
	_actor->_flags &= ~Illusions::ACTOR_FLAG_80;
	_actor->_entryTblPtr = nullptr;
	_actor->_notifyThreadId2 = 0;
}

void Control::setPriority(int16 priority) {
	_priority = priority;
}

uint32 Control::getPriority() {
	uint32 objectId;
	int16 positionY, priority, priority1;
	if (_actor) {
		if (_actor->_parentObjectId && (_actor->_flags & Illusions::ACTOR_FLAG_40)) {
			uint32 parentObjectId = getSubActorParent();
			Control *parentControl = _vm->_dict->getObjectControl(parentObjectId);
			objectId = parentControl->_objectId;
			priority = parentControl->_priority;
			positionY = parentControl->_actor->_position.y;
			priority1 = _priority;
		} else {
			objectId = _objectId;
			positionY = _actor->_position.y;
			priority = _priority;
			priority1 = 50;
		}
	} else {
		positionY = _position.y;
		objectId = _objectId;
		priority = _priority;
		priority1 = 1;
	}

	priority -= 1;
	uint32 p = 50 * priority1 / 100;
	if (p)
		--p;

	positionY = CLIP<int16>(positionY, -5000, 5000);

	return p + 50 * ((objectId & 0x3F) + ((10000 * priority + positionY + 5000) << 6));
}

uint32 Control::getOverlapPriority() {
	if (_vm->getGameId() == kGameIdBBDOU)
		return getPriority();
	return _priority;
}

uint32 Control::getDrawPriority() {
	if (_vm->getGameId() == kGameIdBBDOU)
		return getPriority();
	return (_actor->_position.y + 32768) | (_priority << 16);
}

Common::Point Control::calcPosition(Common::Point posDelta) {
	Common::Point pos;
	if (_actor->_parentObjectId) {
		int16 accuX = 0, accuY = 0;
		Actor *actor = _actor;
		while (actor->_parentObjectId) {
			Control *parentControl = _vm->_dict->getObjectControl(actor->_parentObjectId);
			accuX += parentControl->_subobjectsPos[actor->_linkIndex - 1].x;
			accuY += parentControl->_subobjectsPos[actor->_linkIndex - 1].y;
			actor = parentControl->_actor;
		}
		pos = actor->_position;
		pos.x += accuX * actor->_scale / 100;
		pos.y += accuY * actor->_scale / 100;
		_actor->_position = pos;
		if (!(_flags & 8)) {
			pos.x -= posDelta.x;
			pos.y -= posDelta.y;
		}
	} else {
		pos = _actor->_position;
		if (!(_flags & 8)) {
			pos.x -= posDelta.x;
			pos.y -= posDelta.y;
		}
	}
	return pos;
}

uint32 Control::getSubActorParent() {
	uint32 parentObjectId = _objectId;
	while (1) {
		Actor *actor = _vm->_dict->getObjectControl(parentObjectId)->_actor;
		if (actor->_parentObjectId && (actor->_flags & Illusions::ACTOR_FLAG_40))
			parentObjectId = actor->_parentObjectId;
		else
			break;
	}
	return parentObjectId;
}

void Control::getCollisionRectAccurate(Common::Rect &collisionRect) {

	if (_actor && _actor->_frameIndex) {
		collisionRect = Common::Rect(-_position.x, -_position.y,
			-_position.x + _actor->_surfInfo._dimensions._width - 1,
			-_position.y + _actor->_surfInfo._dimensions._height - 1);
	} else {
		collisionRect = Common::Rect(_bounds._topLeft.x, _bounds._topLeft.y, _bounds._bottomRight.x, _bounds._bottomRight.y);
	}

	if (_actor) {
		if (_actor->_scale != 100) {
			collisionRect.left = collisionRect.left * _actor->_scale / 100;
			collisionRect.top = collisionRect.top * _actor->_scale / 100;
			collisionRect.right = collisionRect.right * _actor->_scale / 100;
			collisionRect.bottom = collisionRect.bottom * _actor->_scale / 100;
		}
		collisionRect.translate(_actor->_position.x, _actor->_position.y);
	}

	if (_flags & 8) {
		Common::Point screenOffs = _vm->_camera->getScreenOffset();
		collisionRect.translate(screenOffs.x, screenOffs.y);
	}

}

void Control::getCollisionRect(Common::Rect &collisionRect) {
	collisionRect = Common::Rect(_bounds._topLeft.x, _bounds._topLeft.y, _bounds._bottomRight.x, _bounds._bottomRight.y);
	if (_actor) {
		if (_actor->_scale != 100) {
			collisionRect.left = collisionRect.left * _actor->_scale / 100;
			collisionRect.top = collisionRect.top * _actor->_scale / 100;
			collisionRect.right = collisionRect.right * _actor->_scale / 100;
			collisionRect.bottom = collisionRect.bottom * _actor->_scale / 100;
		}
		collisionRect.translate(_actor->_position.x, _actor->_position.y);
	}
	if (_flags & 8) {
		Common::Point screenOffs = _vm->_camera->getScreenOffset();
		collisionRect.translate(screenOffs.x, screenOffs.y);
	}
}

void Control::setActorUsePan(int usePan) {
	if (usePan == 1)
		_flags &= ~8;
	else
		_flags |= 8;
}

void Control::setActorFrameIndex(int16 frameIndex) {
	if (frameIndex) {
		_actor->_frameIndex = frameIndex;
		const Frame &frame = (*_actor->_frames)[frameIndex - 1];
		_actor->_surfInfo = frame._surfInfo;
		readPointsConfig(frame._pointsConfig);
		_actor->_flags |= Illusions::ACTOR_FLAG_2000;
		_actor->_flags |= Illusions::ACTOR_FLAG_4000;
		_actor->_newFrameIndex = 0;
	}
}

void Control::stopActor() {
	_actor->_seqCodeIp = nullptr;
	if (_actor->_pathNode) {
		if (_actor->_flags & Illusions::ACTOR_FLAG_400) {
			delete _actor->_pathNode;
			_actor->_flags &= ~Illusions::ACTOR_FLAG_400;
		}
		_actor->_pathNode = nullptr;
		_actor->_pathPoints = 0;
		_actor->_pathPointsCount = 0;
		_actor->_pathPointIndex = 0;
		_actor->_walkCallerThreadId1 = 0;
	}
	if (_vm->getGameId() == kGameIdBBDOU) {
		_vm->notifyThreadId(_actor->_notifyId3C);
		_vm->notifyThreadId(_actor->_notifyThreadId1);
	}
}

void Control::startSequenceActor(uint32 sequenceId, int value, uint32 notifyThreadId) {
	startSequenceActorIntern(sequenceId, value, nullptr, notifyThreadId);
}

void Control::stopSequenceActor() {
	if (_actor->_flags & Illusions::ACTOR_FLAG_40) {
		stopActor();
		_actor->_frameIndex = 0;
		if ((_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) || (_actor->_flags & Illusions::ACTOR_FLAG_1000)) {
			_actor->_flags &= ~Illusions::ACTOR_FLAG_IS_VISIBLE;
			_actor->_flags |= Illusions::ACTOR_FLAG_1000;
		}
	}
	for (uint i = 0; i < kSubObjectsCount; ++i) {
		if (_actor->_subobjects[i]) {
			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
			subControl->stopSequenceActor();
		}
	}
}

void Control::startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 threadId) {
	bool doSeq = true;
	if (_actor->_linkIndex2) {
		Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[_actor->_linkIndex2 - 1]);
		if (subControl->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) {
			if (_actor->_pathNode) {
				doSeq = false;
				subControl->_actor->_notifyThreadId2 = threadId;
				subControl->_actor->_entryTblPtr = entryTblPtr;
				subControl->_actor->_flags |= Illusions::ACTOR_FLAG_80;
				Thread *thread = _vm->_threads->findThread(threadId);
				thread->sendMessage(kMsgClearSequenceId2, 0);
			}
		}
	}
	if (doSeq)
		startSequenceActorIntern(sequenceId, 2, entryTblPtr, threadId);
}

void Control::sequenceActor() {
	if (_actor->_pauseCtr > 0)
		return;

	OpCall opCall;
  	bool sequenceFinished = false;

	opCall._result = 0;
	_actor->_seqCodeValue3 -= _actor->_seqCodeValue1;

	while (_actor->_seqCodeValue3 <= 0 && !sequenceFinished) {
		bool breakInner = false;
		while (!breakInner) {
			//debug(1, "[%08X] SEQ[%08X] op: %08X", _objectId, _actor->_sequenceId, _actor->_seqCodeIp[0]);
			opCall._op = _actor->_seqCodeIp[0] & 0x7F;
			opCall._opSize = _actor->_seqCodeIp[1];
			opCall._code = _actor->_seqCodeIp + 2;
			opCall._deltaOfs = opCall._opSize;
			if (_actor->_seqCodeIp[0] & 0x80)
				breakInner = true;
			execSequenceOpcode(opCall);
			if (opCall._result == 1) {
				sequenceFinished = true;
				breakInner = true;
			} else if (opCall._result == 2) {
				breakInner = true;
			}
			_actor->_seqCodeIp += opCall._deltaOfs;
		}
		_actor->_seqCodeValue3 += _actor->_seqCodeValue2;
	}

	if (_actor->_newFrameIndex != 0) {
		//debug(1, "New frame %d", _actor->_newFrameIndex);
		setActorFrameIndex(_actor->_newFrameIndex);
		if (_vm->getGameId() == kGameIdBBDOU &&
			!(_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) && (_actor->_flags & Illusions::ACTOR_FLAG_1000) && (_objectId != Illusions::CURSOR_OBJECT_ID)) {
			appearActor();
			_actor->_flags &= ~Illusions::ACTOR_FLAG_1000;
		}
		//debug(1, "New frame OK");
	}

	if (sequenceFinished) {
		//debug(1, "Sequence has finished");
		_actor->_seqCodeIp = nullptr;
	}
}

void Control::setActorIndex(int actorIndex) {
	_actor->_actorIndex = actorIndex;
}

void Control::setActorIndexTo1() {
	_actor->_actorIndex = 1;
}

void Control::setActorIndexTo2() {
	_actor->_actorIndex = 2;
}

void Control::startSubSequence(int linkIndex, uint32 sequenceId) {
	Control *linkedControl = _vm->_dict->getObjectControl(_actor->_subobjects[linkIndex - 1]);
	Actor *linkedActor = linkedControl->_actor;
	if (!linkedActor->_entryTblPtr)
		linkedActor->_flags &= ~Illusions::ACTOR_FLAG_80;
	linkedActor->_flags &= ~Illusions::ACTOR_FLAG_400;
	linkedActor->_flags |= Illusions::ACTOR_FLAG_100;
	linkedActor->_sequenceId = sequenceId;
	linkedActor->_notifyThreadId1 = 0;
	linkedActor->_notifyId3C = 0;
	linkedActor->_walkCallerThreadId1 = 0;

	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
	linkedActor->_seqCodeIp = sequence->_sequenceCode;
	linkedActor->_frames = _vm->findActorSequenceFrames(sequence);
	linkedActor->_seqCodeValue3 = 0;
	linkedActor->_seqCodeValue1 = 0;
	linkedActor->_seqCodeValue2 = 600;
	linkedActor->initSequenceStack();
	linkedControl->sequenceActor();
	linkedControl->appearActor();
}

void Control::stopSubSequence(int linkIndex) {
	Control *linkedControl = _vm->_dict->getObjectControl(_actor->_subobjects[linkIndex - 1]);
	Actor *linkedActor = linkedControl->_actor;
	uint32 notifyThreadId2 = _actor->_notifyThreadId2;
	_actor->_linkIndex2 = linkIndex;
	if (_actor->_entryTblPtr) {
		linkedActor->_flags |= Illusions::ACTOR_FLAG_80;
		linkedActor->_entryTblPtr = _actor->_entryTblPtr;
		linkedActor->_notifyThreadId2 = _actor->_notifyThreadId2;
		linkedActor->_seqCodeValue1 = _actor->_seqCodeValue1;
		linkedActor->_seqCodeValue3 = _actor->_seqCodeValue3;
		_actor->_flags &= ~Illusions::ACTOR_FLAG_80;
		_actor->_entryTblPtr = nullptr;
		_actor->_notifyThreadId1 = 0;
		_actor->_notifyThreadId2 = 0;
	}
	if (notifyThreadId2) {
		Thread *talkThread = _vm->_threads->findThread(notifyThreadId2);
		talkThread->sendMessage(kMsgClearSequenceId2, 0);
	}
}

void Control::startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 callerThreadId1, uint32 callerThreadId2) {

	PointArray *pathNode;
	ActorType *actorType = _vm->_dict->findActorType(_actorTypeId);

	_actor->_pathAngle = 0;
	_actor->_pathFlag50 = false;
	_actor->_seqCodeValue3 = 0;
	_actor->_seqCodeValue1 = 0;
	_actor->_pathInitialPosFlag = true;

	uint newFacing;
	if (_vm->calcPointDirection(_actor->_position, destPt, newFacing))
		faceActor(newFacing);

	if (actorType->_value1E)
		_actor->_pathCtrY = actorType->_value1E;
	else
		_actor->_pathCtrY = 140;

	pathNode = createPath(destPt);

	if (pathNode->size() == 1 &&
		_actor->_position.x == (*pathNode)[0].x &&
		_actor->_position.y == (*pathNode)[0].y) {
		delete pathNode;
		_vm->notifyThreadId(callerThreadId2);
	} else {
		_actor->_posXShl = _actor->_position.x << 16;
		_actor->_posYShl = _actor->_position.y << 16;
		startSequenceActor(sequenceId, 1, 0);
		_actor->_pathNode = pathNode;
		_actor->_pathPointsCount = pathNode->size();
		_actor->_pathPoints = pathNode->size();
		_actor->_flags |= Illusions::ACTOR_FLAG_400;
		_actor->_walkCallerThreadId1 = callerThreadId1;
		_vm->notifyThreadId(_actor->_notifyId3C);
		_actor->_notifyId3C = callerThreadId2;
		_actor->_pathPointIndex = 0;
		_vm->_input->discardEvent(kEventSkip);
	}

}

PointArray *Control::createPath(Common::Point destPt) {
	PointArray *walkPoints = (_actor->_flags & Illusions::ACTOR_FLAG_HAS_WALK_POINTS) ? _actor->_pathWalkPoints->_points : nullptr;
	PathLines *walkRects = (_actor->_flags & Illusions::ACTOR_FLAG_HAS_WALK_RECTS) ? _actor->_pathWalkRects->_rects : nullptr;
	PathFinder pathFinder;
	WidthHeight bgDimensions = _vm->_backgroundInstances->getMasterBgDimensions();
	PointArray *path = pathFinder.findPath(_vm->_camera, _actor->_position, destPt, walkPoints, walkRects, bgDimensions);
	return path;
}

void Control::updateActorMovement(uint32 deltaTime) {
	// TODO This needs some cleanup

	static const int16 kAngleTbl[] = {60, 0, 120, 0, 60, 0, 120, 0};
	bool fastWalked = false;

	do {

		if (!fastWalked && _vm->testMainActorFastWalk(this)) {
			fastWalked = true;
			disappearActor();
			_actor->_flags |= Illusions::ACTOR_FLAG_8000;
			_actor->_seqCodeIp = nullptr;
			deltaTime = 2;
		}

		if (_vm->testMainActorCollision(this))
			break;

		Common::Point prevPt;
		if (_actor->_pathPointIndex == 0) {
			if (_actor->_pathInitialPosFlag) {
				_actor->_pathCtrX = 0;
				_actor->_pathInitialPos = _actor->_position;
				_actor->_pathInitialPosFlag = false;
			}
			prevPt = _actor->_pathInitialPos;
		} else {
			prevPt = (*_actor->_pathNode)[_actor->_pathPointIndex - 1];
		}

		Common::Point currPt = (*_actor->_pathNode)[_actor->_pathPointIndex];

		int16 deltaX = currPt.x - prevPt.x;
		int16 deltaY = currPt.y - prevPt.y;

		if (!_actor->_pathFlag50) {

			FixedPoint16 angle;
			if (currPt.x == prevPt.x) {
				if (prevPt.y >= currPt.y)
					angle = fixedMul(-0x5A0000, 0x478);
				else
					angle = fixedMul(0x5A0000, 0x478);
			} else {
				angle = fixedAtan(fixedDiv(deltaY << 16, deltaX << 16));
			}
			_actor->_pathAngle = angle;

			int16 v13 = (fixedTrunc(fixedMul(angle, 0x394BB8)) + 360) % 360; // 0x394BB8 is 180 / pi
			if (deltaX >= 0)
				v13 += 180;
			v13 = (v13 + 90) % 360;
			int16 v15 = kAngleTbl[0] / -2;
			uint newFacing = 1;
			for (uint i = 0; i < 8; ++i) {
				v15 += kAngleTbl[i];
				if (v13 < v15) {
					newFacing = 1 << i;
					break;
				}
			}
			if (newFacing != _actor->_facing) {
				refreshSequenceCode();
				faceActor(newFacing);
			}

			_actor->_pathFlag50 = true;

		}

		FixedPoint16 deltaX24, deltaY24;

		if (_actor->_flags & Illusions::ACTOR_FLAG_400) {

			FixedPoint16 v20 = fixedMul((deltaTime + _actor->_pathCtrX) << 16, _actor->_pathCtrY << 16);
			FixedPoint16 v21 = fixedDiv(v20, 100 << 16);
			FixedPoint16 v22 = fixedMul(v21, _actor->_scale << 16);
			FixedPoint16 v23 = fixedDiv(v22, 100 << 16);
			_actor->_seqCodeValue1 = 100 * _actor->_pathCtrY * deltaTime / 100;
			if (v23) {
				FixedPoint16 prevDistance = fixedDistance(prevPt.x << 16, prevPt.y << 16, _actor->_posXShl, _actor->_posYShl);
				FixedPoint16 distance = prevDistance + v23;
				if (prevPt.x > currPt.x)
					distance = -distance;
				deltaX24 = fixedMul(fixedCos(_actor->_pathAngle), distance);
				deltaY24 = fixedMul(fixedSin(_actor->_pathAngle), distance);
			} else {
				deltaX24 = _actor->_posXShl - (prevPt.x << 16);
				deltaY24 = _actor->_posYShl - (prevPt.y << 16);
			}
		} else {
			if (100 * (int)deltaTime <= _actor->_seqCodeValue2)
				break;
			deltaX24 = deltaX << 16;
			deltaY24 = deltaY << 16;
		}

		if (ABS(deltaX24) < ABS(deltaX << 16) ||
			ABS(deltaY24) < ABS(deltaY << 16)) {
			FixedPoint16 newX = (prevPt.x << 16) + deltaX24;
			FixedPoint16 newY = (prevPt.y << 16) + deltaY24;
			if (newX == _actor->_posXShl &&	newY == _actor->_posYShl) {
				_actor->_pathCtrX += deltaTime;
			} else {
				_actor->_pathCtrX = 0;
				_actor->_posXShl = newX;
				_actor->_posYShl = newY;
				_actor->_position.x = fixedTrunc(_actor->_posXShl);
				_actor->_position.y = fixedTrunc(_actor->_posYShl);
			}
		} else {
			_actor->_position = currPt;
			_actor->_posXShl = _actor->_position.x << 16;
			_actor->_posYShl = _actor->_position.y << 16;
			--_actor->_pathPointsCount;
			++_actor->_pathPointIndex;
			++_actor->_pathPoints;
			_actor->_pathInitialPosFlag = true;
			if (_actor->_pathPointsCount == 0) {
				if (_actor->_flags & Illusions::ACTOR_FLAG_400) {
					delete _actor->_pathNode;
					_actor->_flags &= ~Illusions::ACTOR_FLAG_400;
				}
				_actor->_pathNode = nullptr;
				_actor->_pathPoints = 0;
				_actor->_pathPointsCount = 0;
				_actor->_pathPointIndex = 0;
				if (_actor->_notifyId3C) {
					_vm->notifyThreadId(_actor->_notifyId3C);
					_actor->_walkCallerThreadId1 = 0;
				}
				fastWalked = false;
			}
			_actor->_pathFlag50 = false;
		}

	} while (fastWalked);

}

void Control::refreshSequenceCode() {
	Sequence *sequence = _vm->_dict->findSequence(_actor->_sequenceId);
	_actor->_seqCodeIp = sequence->_sequenceCode;
}

void Control::getActorFrameDimensions(WidthHeight &dimensions) {
	dimensions._width = _actor->_surface->w;
	dimensions._height = _actor->_surface->h;
}

void Control::drawActorRect(const Common::Rect r, byte color) {
	_vm->_screen->fillSurfaceRect(_actor->_surface, r, color);
	_actor->_flags |= Illusions::ACTOR_FLAG_4000;
}

void Control::fillActor(byte color) {
	_vm->_screen->fillSurface(_actor->_surface, color);
	_actor->_flags |= Illusions::ACTOR_FLAG_4000;
}

bool Control::isPixelCollision(Common::Point &pt) {
	Frame *frame = &(*_actor->_frames)[_actor->_frameIndex - 1];
	return _vm->_screen->isSpritePixelSolid(pt, _position, _actor->_position,
		_actor->_surfInfo, _actor->_scale, frame->_flags, frame->_compressedPixels);
}

void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId) {
	stopActor();

	_actor->_flags &= ~Illusions::ACTOR_FLAG_80;
	_actor->_flags &= ~Illusions::ACTOR_FLAG_400;
	_actor->_flags |= Illusions::ACTOR_FLAG_100;

	sequenceId = _actor->_defaultSequences.use(sequenceId);

	_actor->_sequenceId = sequenceId;
	_actor->_notifyThreadId1 = notifyThreadId;
	_actor->_notifyId3C = 0;
	_actor->_walkCallerThreadId1 = 0;
	_actor->_entryTblPtr = nullptr;

	Sequence *sequence = _vm->_dict->findSequence(sequenceId);

	if (!sequence && _vm->getGameId() == kGameIdDuckman) {
		//debug(1, "Load external sequence...");
		_vm->_resSys->loadResource(0x00060000 | (sequenceId & 0xFFFF), _vm->getCurrentScene(), 0);
		sequence = _vm->_dict->findSequence(sequenceId);
		_actor->_flags |= Illusions::ACTOR_FLAG_800;
	}

	_actor->_seqCodeIp = sequence->_sequenceCode;
	_actor->_frames = _vm->findActorSequenceFrames(sequence);

	_actor->_seqCodeValue3 = 0;
	_actor->_seqCodeValue1 = 0;

	if (_vm->getGameId() == kGameIdBBDOU) {
		_actor->_seqCodeValue2 = value == 1 ? 350 : 600;
	} else if (_vm->getGameId() == kGameIdDuckman) {
		_actor->_seqCodeValue2 = value == 1 ? 350 : 750;
	}

	_actor->initSequenceStack();

	if (_vm->getGameId() == kGameIdBBDOU)
		stopSequenceActor();

	_actor->_linkIndex2 = 0;

	if (entryTblPtr) {
		_actor->_flags |= Illusions::ACTOR_FLAG_80;
		_actor->_entryTblPtr = entryTblPtr;
		if (_vm->getGameId() == kGameIdBBDOU) {
			_actor->_notifyThreadId1 = 0;
			_actor->_notifyThreadId2 = notifyThreadId;
		}
	}

	if (_vm->getGameId() == kGameIdBBDOU)
		sequenceActor();

}

void Control::execSequenceOpcode(OpCall &opCall) {
	_vm->_controls->_sequenceOpcodes->execOpcode(this, opCall);
}

// Controls

Controls::Controls(IllusionsEngine *vm)
	: _vm(vm) {
	_sequenceOpcodes = new SequenceOpcodes(_vm);
	_nextTempObjectId = 0;
}

Controls::~Controls() {
	delete _sequenceOpcodes;
	destroyControls();
}

void Controls::placeBackgroundObject(BackgroundObject *backgroundObject) {
	Control *control = newControl();
	control->_objectId = backgroundObject->_objectId;
	control->_flags = backgroundObject->_flags;
	control->_priority = backgroundObject->_priority;
	control->readPointsConfig(backgroundObject->_pointsConfig);
	control->activateObject();
	_controls.push_front(control);
	_vm->_dict->setObjectControl(control->_objectId, control);
	debug(0, "Added background control. objectId: %08X", control->_objectId);
}

void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId) {
	Control *control = newControl();
	Actor *actor = newActor();
	ActorType *actorType = _vm->_dict->findActorType(actorTypeId);

	control->_objectId = objectId;
	control->_flags = actorType->_flags;
	control->_priority = actorType->_priority;
	control->readPointsConfig(actorType->_pointsConfig);
	control->_actorTypeId = actorTypeId;
	control->_actor = actor;

	if (_vm->isCursorObject(actorTypeId, objectId))
		_vm->setCursorControlRoutine(control);

	if (actorType->_surfInfo._dimensions._width > 0 || actorType->_surfInfo._dimensions._height > 0) {
		actor->createSurface(actorType->_surfInfo);
	} else {
		actor->_flags |= Illusions::ACTOR_FLAG_200;
	}

	actor->_position = placePt;
	actor->_position2 = placePt;
	Common::Point currPan = _vm->_camera->getCurrentPan();
	if (!_vm->calcPointDirection(placePt, currPan, actor->_facing))
		actor->_facing = 64;
	actor->_scale = actorType->_scale;
	actor->_namedPoints = &actorType->_namedPoints;

	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
	if (actorType->_pathWalkPointsIndex) {
		actor->_pathWalkPoints = bgRes->getPathWalkPoints(actorType->_pathWalkPointsIndex - 1);
		actor->_flags |= Illusions::ACTOR_FLAG_HAS_WALK_POINTS;
	}

	if (actorType->_scaleLayerIndex) {
		actor->_scaleLayer = bgRes->getScaleLayer(actorType->_scaleLayerIndex - 1);
		actor->_flags |= Illusions::ACTOR_FLAG_SCALED;
	}

	if (actorType->_pathWalkRectIndex) {
		actor->_pathWalkRects = bgRes->getPathWalkRects(actorType->_pathWalkRectIndex - 1);
		actor->_flags |= Illusions::ACTOR_FLAG_HAS_WALK_RECTS;
	}

	if (actorType->_priorityLayerIndex) {
		actor->_priorityLayer = bgRes->getPriorityLayer(actorType->_priorityLayerIndex - 1);
		actor->_flags |= Illusions::ACTOR_FLAG_PRIORITY;
	}

	if (actorType->_regionLayerIndex) {
		actor->_regionLayer = bgRes->getRegionLayer(actorType->_regionLayerIndex - 1);
		actor->_flags |= Illusions::ACTOR_FLAG_REGION;
	}

	actor->_pathCtrY = 140;

	_controls.push_front(control);
	_vm->_dict->setObjectControl(objectId, control);

	if (_vm->getGameId() == kGameIdDuckman) {
		control->appearActor();
	} else if (_vm->getGameId() == kGameIdBBDOU) {
		control->_flags |= 0x01;
		actor->_flags |= Illusions::ACTOR_FLAG_1000;
	}

	if (_vm->isCursorObject(actorTypeId, objectId))
		_vm->placeCursorControl(control, sequenceId);

	// TODO HACK at least we should restrict this to the sequenceId
	control->setActorIndex(1);

	control->startSequenceActor(sequenceId, 2, notifyThreadId);
}

void Controls::placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority) {
	Control *control = newControl();
	Actor *actor = newActor();
	control->_flags = 0;
	control->_priority = priority;
	control->_objectId = objectId;
	control->_bounds._topLeft.x = 0;
	control->_bounds._topLeft.y = 0;
	control->_bounds._bottomRight.x = dimensions._width - 1;
	control->_bounds._bottomRight.y = dimensions._height - 1;
	control->_feetPt.x = dimensions._width / 2;
	control->_feetPt.y = dimensions._height / 2;
	control->_position.x = 0;
	control->_position.y = 0;
	control->_actorTypeId = 0x50004;
	control->_actor = actor;
	actor->setControlRoutine(nullptr);
	actor->_surfInfo._pixelSize = dimensions._width * dimensions._height;
	actor->_surfInfo._dimensions = dimensions;
	actor->createSurface(actor->_surfInfo);
	actor->_position = placePt;
	actor->_position2 = placePt;
	actor->_facing = 64;
	actor->_scale = 100;
	actor->_namedPoints = nullptr;
	actor->_pathCtrY = 140;

	_controls.push_front(control);
	_vm->_dict->setObjectControl(objectId, control);
	control->appearActor();
}

void Controls::placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags) {
	Control *control = newControl();
	control->_flags = flags;
	control->_feetPt = feetPt;
	control->_priority = priority;
	control->_objectId = objectId;
	control->_bounds._topLeft = feetPt;
	control->_bounds._bottomRight = pt;
	control->_position.x = 0;
	control->_position.y = 0;
	control->_actorTypeId = 0;
	control->_actor = nullptr;
	_controls.push_front(control);
	_vm->_dict->setObjectControl(objectId, control);
}

void Controls::placeSubActor(uint32 objectId, int linkIndex, uint32 actorTypeId, uint32 sequenceId) {
	Control *parentControl = _vm->_dict->getObjectControl(objectId);
	uint32 tempObjectId = newTempObjectId();
	placeActor(actorTypeId, Common::Point(0, 0), sequenceId, tempObjectId, 0);
	parentControl->_actor->_subobjects[linkIndex - 1] = tempObjectId;
	Actor *subActor = _vm->_dict->getObjectControl(tempObjectId)->_actor;
	subActor->_flags |= Illusions::ACTOR_FLAG_40;
	subActor->_parentObjectId = parentControl->_objectId;
	subActor->_linkIndex = linkIndex;
}

void Controls::placeDialogItem(uint16 objectNum, uint32 actorTypeId, uint32 sequenceId, Common::Point placePt, int16 choiceJumpOffs) {
	Control *control = newControl();
	Actor *actor = newActor();
	ActorType *actorType = _vm->_dict->findActorType(actorTypeId);
	control->_flags = 0xC;
	control->_priority = actorType->_priority;
	control->_objectId = objectNum | 0x40000;
	control->readPointsConfig(actorType->_pointsConfig);
	control->_actorTypeId = actorTypeId;
	control->_actor = actor;
	actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, Controls>(this, &Controls::dialogItemControlRoutine));
	actor->_choiceJumpOffs = choiceJumpOffs;
	actor->createSurface(actorType->_surfInfo);
	actor->_position = placePt;
	actor->_position2 = placePt;
	actor->_scale = actorType->_scale;
	actor->_color = actorType->_color;
	_controls.push_front(control);
	control->appearActor();
	control->startSequenceActor(sequenceId, 2, 0);
	control->setActorIndex(1);
}

void Controls::destroyControls() {
	ItemsIterator it = _controls.begin();
	while (it != _controls.end()) {
		destroyControlInternal(*it);
		it = _controls.erase(it);
	}
}

void Controls::destroyActiveControls() {
	ItemsIterator it = _controls.begin();
	while (it != _controls.end()) {
		if ((*it)->_pauseCtr <= 0) {
			destroyControlInternal(*it);
			it = _controls.erase(it);
		} else
			++it;
	}
}

void Controls::destroyControlsBySceneId(uint32 sceneId) {
	ItemsIterator it = _controls.begin();
	while (it != _controls.end()) {
		if ((*it)->_sceneId == sceneId) {
			destroyControlInternal(*it);
			it = _controls.erase(it);
		} else
			++it;
	}
}

void Controls::destroyDialogItems() {
	ItemsIterator it = _controls.begin();
	while (it != _controls.end()) {
		if (((*it)->_pauseCtr == 0) && ((*it)->_flags & 4)) {
			destroyControlInternal(*it);
			it = _controls.erase(it);
		} else
			++it;
	}
}

void Controls::threadIsDead(uint32 threadId) {
	for (auto &control : _controls) {
		if (control->_actor &&
			(control->_actor->_notifyThreadId1 == threadId || control->_actor->_notifyId3C == threadId)) {
			control->_actor->_notifyThreadId1 = 0;
			control->_actor->_notifyId3C = 0;
		}
	}
}

void Controls::pauseControls() {
	for (auto &control : _controls) {
		++control->_pauseCtr;
		if (control->_pauseCtr == 1)
			control->pause();
	}
}

void Controls::unpauseControls() {
	for (auto &control : _controls) {
		--control->_pauseCtr;
		if (control->_pauseCtr == 0)
			control->unpause();
	}
}

void Controls::pauseControlsBySceneId(uint32 sceneId) {
	for (auto &control : _controls) {
		if (control->_sceneId == sceneId) {
			++control->_pauseCtr;
			if (control->_pauseCtr == 1)
				control->pause();
		}
	}
}

void Controls::unpauseControlsBySceneId(uint32 sceneId) {
	for (auto &control : _controls) {
		if (control->_sceneId == sceneId) {
			--control->_pauseCtr;
			if (control->_pauseCtr == 0)
				control->unpause();
		}
	}
}

bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority) {
	Control *foundControl = nullptr;
	uint32 foundPriority = 0;
	uint32 minPriorityExt = _vm->getPriorityFromBase(minPriority);

	for (auto &testControl : _controls) {
		if (testControl != control && testControl->_pauseCtr == 0 &&
			(testControl->_flags & 1) && !(testControl->_flags & 0x10) &&
			(!testControl->_actor || (testControl->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE))) {
			Common::Rect collisionRect;
			testControl->getCollisionRect(collisionRect);
			if (!collisionRect.isEmpty() && collisionRect.contains(pt)) {
				uint32 testPriority = testControl->getOverlapPriority();
				if ((!foundControl || foundPriority < testPriority) &&
					testPriority >= minPriorityExt) {
					foundControl = testControl;
					foundPriority = testPriority;
				}
			}
		}
	}

	if (foundControl) {
		if (foundControl->_actor && foundControl->_actor->_parentObjectId && (foundControl->_actor->_flags & Illusions::ACTOR_FLAG_40)) {
			uint32 parentObjectId = foundControl->getSubActorParent();
			foundControl = _vm->_dict->getObjectControl(parentObjectId);
		}
		*outOverlappedControl = foundControl;
	}

	return foundControl != nullptr;
}

bool Controls::getOverlappedObjectAccurate(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority) {
	Control *foundControl = nullptr;
	uint32 foundPriority = 0;
	uint32 minPriorityExt = _vm->getPriorityFromBase(minPriority);

	for (auto &testControl : _controls) {
		if (testControl != control && testControl->_pauseCtr == 0 &&
			(testControl->_flags & 1) && !(testControl->_flags & 0x10) &&
			(!testControl->_actor || (testControl->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE))) {
			Common::Rect collisionRect;
			testControl->getCollisionRectAccurate(collisionRect);
			if (!collisionRect.isEmpty() && collisionRect.contains(pt) &&
				(!testControl->_actor || testControl->isPixelCollision(pt))) {
				uint32 testPriority = testControl->getOverlapPriority();
				if ((!foundControl || foundPriority < testPriority) &&
					testPriority >= minPriorityExt) {
					foundControl = testControl;
					foundPriority = testPriority;
				}
			}
		}
	}

	if (foundControl) {
		if (foundControl->_actor && foundControl->_actor->_parentObjectId && (foundControl->_actor->_flags & Illusions::ACTOR_FLAG_40)) {
			uint32 parentObjectId = foundControl->getSubActorParent();
			foundControl = _vm->_dict->getObjectControl(parentObjectId);
		}
		*outOverlappedControl = foundControl;
	}

	return foundControl != nullptr;
}

bool Controls::getDialogItemAtPos(Control *control, Common::Point pt, Control **outOverlappedControl) {
	Control *foundControl = nullptr;
	for (auto &testControl : _controls) {
		if (testControl != control && testControl->_pauseCtr == 0 &&
			(testControl->_flags & 1) && (testControl->_flags & 4)) {
			Common::Rect collisionRect;
			testControl->getCollisionRect(collisionRect);
			if (!collisionRect.isEmpty() && collisionRect.contains(pt) &&
				(!foundControl || foundControl->_priority < testControl->_priority))
				foundControl = testControl;
		}
	}
	*outOverlappedControl = foundControl;
	return foundControl != nullptr;
}

bool Controls::getOverlappedWalkObject(Control *control, Common::Point pt, Control **outOverlappedControl) {
	Control *foundControl = nullptr;
	for (auto &testControl : _controls) {
		if (testControl != control && testControl->_pauseCtr == 0 &&
			(testControl->_flags & 1)) {
			Common::Rect collisionRect;
			testControl->getCollisionRect(collisionRect);
			if (!collisionRect.isEmpty() && collisionRect.contains(pt) &&
				(!foundControl || foundControl->_priority < testControl->_priority))
				foundControl = testControl;
		}
	}
	if (foundControl)
		*outOverlappedControl = foundControl;
	return foundControl != nullptr;
}

void Controls::destroyControl(Control *control) {
	_controls.remove(control);
	destroyControlInternal(control);
}

bool Controls::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
	for (auto &control : _controls) {
		if (control->_pauseCtr == 0 && control->_actor && control->_actor->findNamedPoint(namedPointId, pt))
			return true;
	}
	return false;
}

void Controls::actorControlRoutine(Control *control, uint32 deltaTime) {

	Actor *actor = control->_actor;

	if (actor->_pauseCtr > 0)
		return;

	if (control->_actor->_pathNode) {
		control->updateActorMovement(deltaTime);
	} else {
		actor->_seqCodeValue1 = 100 * deltaTime;
	}

	if (actor->_flags & Illusions::ACTOR_FLAG_SCALED) {
		int scale = actor->_scaleLayer->getScale(actor->_position);
		control->setActorScale(scale);
	}

	if (actor->_flags & Illusions::ACTOR_FLAG_PRIORITY) {
		int16 priority = actor->_priorityLayer->getPriority(actor->_position);
		if (priority)
			control->setPriority(priority + 1);
	}

	if (actor->_flags & Illusions::ACTOR_FLAG_REGION) {
		// Update transition sequence
		int regionIndex = actor->_regionLayer->getRegionIndex(actor->_position);
		if (actor->_regionIndex != regionIndex) {
			if (regionIndex) {
				uint32 savedSequenceId = actor->_sequenceId;
				byte *savedSeqCodeIp = actor->_seqCodeIp;
				int savedSeqCodeValue1 = actor->_seqCodeValue1;
				int savedSeqCodeValue3 = actor->_seqCodeValue3;
				uint32 regionSequenceId = actor->_regionLayer->getRegionSequenceId(regionIndex);
				//debug(1, "Running transition sequence %08X", regionSequenceId);
				Sequence *sequence = _vm->_dict->findSequence(regionSequenceId);
				actor->_sequenceId = regionSequenceId;
				actor->_seqCodeIp = sequence->_sequenceCode;
				actor->_seqCodeValue3 = 0;
				control->sequenceActor();
				actor->_sequenceId = savedSequenceId;
				actor->_seqCodeIp = savedSeqCodeIp;
				actor->_seqCodeValue3 = savedSeqCodeValue3;
				actor->_seqCodeValue1 = savedSeqCodeValue1;
			}
			actor->_regionIndex = regionIndex;
		}
	}

}

void Controls::dialogItemControlRoutine(Control *control, uint32 deltaTime) {
	Actor *actor = control->_actor;
	if (actor->_pauseCtr <= 0)
		actor->_seqCodeValue1 = 100 * deltaTime;
}

Actor *Controls::newActor() {
	return new Actor(_vm);
}

Control *Controls::newControl() {
	return new Control(_vm);
}

uint32 Controls::newTempObjectId() {
	uint32 nextTempObjectId1 = _nextTempObjectId;
	uint32 nextTempObjectId2 = _nextTempObjectId + 0x1000;
	if (nextTempObjectId2 > 0xFFFF) {
		nextTempObjectId1 = 0;
		nextTempObjectId2 = 0x1000;
	}
	_nextTempObjectId = nextTempObjectId1 + 1;
	return nextTempObjectId2 | 0x40000;
}

void Controls::destroyControlInternal(Control *control) {

	if ((_vm->getGameId() == kGameIdBBDOU || !(control->_flags & 4)) && control->_pauseCtr <= 0)
		_vm->_dict->setObjectControl(control->_objectId, nullptr);

	if ((_vm->getGameId() == kGameIdBBDOU || !(control->_flags & 4)) && control->_objectId == Illusions::CURSOR_OBJECT_ID && control->_pauseCtr <= 0)
		_vm->setCursorControl(nullptr);

	if (control->_actor) {
		if (control->_actor->_pathNode && (control->_actor->_flags & Illusions::ACTOR_FLAG_400))
			delete control->_actor->_pathNode;
		if (!(control->_actor->_flags & Illusions::ACTOR_FLAG_200))
			control->_actor->destroySurface();
		delete control->_actor;
		control->_actor = nullptr;
	}

	delete control;
}

void Controls::disappearActors() {
	for (auto &control : _controls) {
		if (control->_flags & 4 && control->_pauseCtr == 0) {
			control->disappearActor();
		}
	}
	Control *control = _vm->_dict->getObjectControl(0x40148);
	if (control) {
		control->disappearActor();
	}
}

void Controls::appearActors() {
	for (auto &control : _controls) {
		if (control->_flags & 4 && control->_pauseCtr == 0) {
			control->appearActor();
		}
	}
	Control *control = _vm->_dict->getObjectControl(0x40148);
	if (control) {
		control->appearActor();
	}
}

void Controls::pauseActors(uint32 objectId) {
	for (auto &control : _controls) {
		if (control->_actor && control->_objectId != objectId) {
			control->_actor->pause();
		}
	}
}

void Controls::unpauseActors(uint32 objectId) {
	for (auto &control : _controls) {
		if (control->_actor && control->_objectId != objectId) {
			control->_actor->unpause();
		}
	}
	_vm->_unpauseControlActorFlag = true;
}


} // End of namespace Illusions
