/* ScummVM - Scumm Interpreter
 * Copyright (C) 2004 Ivan Dubrov
 * Copyright (C) 2004-2006 The ScummVM project
 *
 * 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://svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-9-0/engines/gob/goblin_v2.cpp $
 * $Id: goblin_v2.cpp 23065 2006-06-12 21:33:30Z drmccoy $
 *
 */

#include "common/stdafx.h"
#include "common/endian.h"

#include "gob/gob.h"
#include "gob/goblin.h"
#include "gob/mult.h"
#include "gob/game.h"
#include "gob/scenery.h"
#include "gob/map.h"

namespace Gob {

Goblin_v2::Goblin_v2(GobEngine *vm) : Goblin_v1(vm) {
	_gobsCount = -1;
	_rotStates[0][0] = 0; _rotStates[0][1] = 18; _rotStates[0][2] = 19; _rotStates[0][3] = 20;
	_rotStates[1][0] = 13; _rotStates[1][1] = 2; _rotStates[1][2] = 12; _rotStates[1][3] = 14;
	_rotStates[2][0] = 16; _rotStates[2][1] = 15; _rotStates[2][2] = 4; _rotStates[2][3] = 17;
	_rotStates[3][0] = 23; _rotStates[3][1] = 21; _rotStates[3][2] = 22; _rotStates[3][3] = 6;
}

void Goblin_v2::freeObjects(void) {
	int i;

	if (_gobsCount < 0)
		return;

	for (i = 0; i < _gobsCount; i++) {
		delete[] _vm->_mult->_objects[i].goblinStates[0];
		delete[] _vm->_mult->_objects[i].goblinStates;
	}
	for (i = 0; i < _vm->_goblin->_soundSlotsCount; i++)
		if ((_vm->_goblin->_soundSlots[i] & 0x8000) == 0)
			_vm->_game->freeSoundSlot(_vm->_goblin->_soundSlots[i]);
//	delete[] off_2F2AB;
	_gobsCount = -1;
}

void Goblin_v2::placeObject(Gob_Object *objDesc, char animated,
		int16 index, int16 x, int16 y, int16 state) {
	Mult::Mult_Object *obj;
	Mult::Mult_AnimData *objAnim;
	int16 layer;
	int16 animation;

	obj = &_vm->_mult->_objects[index];
	objAnim = obj->pAnimData;

	obj->goblinX = x;
	obj->goblinY = y;
	objAnim->order = y;

	if (state == -1) {
		objAnim->frame = 0;
		objAnim->isPaused = 0;
		objAnim->isStatic = 0;
		objAnim->newCycle = 0;
		_vm->_scenery->updateAnim(objAnim->layer, 0, objAnim->animation, 0,
				*obj->pPosX, *obj->pPosY, 0);
		if (!_vm->_map->_bigTiles)
			*obj->pPosY = (y + 1) * _vm->_map->_tilesHeight
				- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
		else
			*obj->pPosY = ((y + 1) / 2) * _vm->_map->_tilesHeight
				- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
		*obj->pPosX = x * _vm->_map->_tilesWidth;
	} else {
		if (obj->goblinStates[state] != 0) {
			layer = obj->goblinStates[state][0].layer;
			animation = obj->goblinStates[state][0].animation;
			objAnim->state = state;
			objAnim->layer = layer;
			objAnim->animation = animation;
			objAnim->frame = 0;
			objAnim->isPaused = 0;
			objAnim->isStatic = 0;
			objAnim->newCycle = _vm->_scenery->_animations[animation].layers[layer].framesCount;
			_vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
			if (!_vm->_map->_bigTiles)
				*obj->pPosY = (y + 1) * _vm->_map->_tilesHeight
					- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
			else
				*obj->pPosY = ((y + 1) / 2) * _vm->_map->_tilesHeight
					- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
			*obj->pPosX = x * _vm->_map->_tilesWidth;
			initiateMove(obj);
		} else
			initiateMove(obj);
	}
}

void Goblin_v2::initiateMove(Mult::Mult_Object *obj) {
	obj->destX = obj->gobDestX;
	obj->destY = obj->gobDestY;
	_vm->_map->findNearestToDest(obj);
	_vm->_map->findNearestToGob(obj);
	_vm->_map->optimizePoints(obj, obj->goblinX, obj->goblinY);
	obj->pAnimData->pathExistence = _vm->_map->checkDirectPath(obj,
			obj->goblinX, obj->goblinY, obj->gobDestX, obj->gobDestY);
	if (obj->pAnimData->pathExistence == 3) {
		obj->destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
		obj->destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
	}
}

void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16 nextAct) {
	Mult::Mult_AnimData *animData;
	int16 framesCount;
	int16 gobX;
	int16 gobY;
	int16 gobDestX;
	int16 gobDestY;
	int16 destX;
	int16 destY;
	int16 dir;

	dir = 0;
	animData = obj->pAnimData;
	framesCount =
		_vm->_scenery->_animations[(int)animData->animation].layers[animData->layer].framesCount;
	gobX = obj->goblinX;
	gobY = obj->goblinY;
	animData->order = gobY;
	gobDestX = obj->gobDestX;
	gobDestY = obj->gobDestY;
	animData->field_13 = gobDestX;
	animData->field_14 = gobDestY;
	destX = obj->destX;
	destY = obj->destY;

	if (animData->pathExistence == 1) {
		dir = _vm->_map->getDirection(gobX, gobY, destX, destY);
		if (dir == 0)
			animData->pathExistence = 0;
		if ((gobX == destX) && (gobY == destY))
			animData->pathExistence = 4;
	} else if (animData->pathExistence == 3) {
		if ((gobX == gobDestX) && (gobY == gobDestY)) {
			animData->pathExistence = 4;
			destX = gobDestX;
			destY = gobDestY;
		} else {
			if (_vm->_map->checkDirectPath(obj, gobX, gobY, gobDestX, gobDestY) != 1) {
				if ((gobX == destX) && (gobY == destY)) {
					if (obj->nearestWayPoint > obj->nearestDest) {
						_vm->_map->optimizePoints(obj, gobX, gobY);
						destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
						destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
						if (_vm->_map->checkDirectPath(obj, gobX, gobY, destX, destY) == 3) {
							WRITE_VAR(56, 1);
							animData->pathExistence = 0;
						}
						if (obj->nearestWayPoint > obj->nearestDest)
							obj->nearestWayPoint--;
					} else if (obj->nearestWayPoint < obj->nearestDest) { // loc_10E96
						_vm->_map->optimizePoints(obj, gobX, gobY);
						destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
						destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
						if (_vm->_map->checkDirectPath(obj, gobX, gobY, destX, destY) == 3) {
							WRITE_VAR(56, 1);
							animData->pathExistence = 0;
						}
						if (obj->nearestWayPoint < obj->nearestDest)
							obj->nearestWayPoint++;
					} else {
						if ((_vm->_map->checkDirectPath(obj, gobX, gobY, gobDestX, gobDestY) == 3) &&
								(_vm->_map->getPass(gobDestX, gobDestY) != 0)) {
							destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
							destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
							WRITE_VAR(56, 1);
						} else {
							animData->pathExistence = 1;
							destX = gobDestX;
							destY = gobDestY;
						}
					}
				}
			} else {
				destX = gobDestX;
				destY = gobDestY;
			}
			dir = _vm->_map->getDirection(gobX, gobY, destX, destY);
		}
	}

	obj->goblinX = gobX;
	obj->goblinY = gobY;
	obj->gobDestX = gobDestX;
	obj->gobDestY = gobDestY;
	obj->destX = destX;
	obj->destY = destY;

	switch (dir) {
	case Map::kDirNW:
		animData->nextState = 1;
		if (_vm->_map->_screenWidth == 640) {
			if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
				animData->nextState = 40;
			if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10)
				animData->nextState = 1;
		}
		break;

	case Map::kDirN:
		animData->nextState = animData->curLookDir == 2 ? 2 : rotateState(2, animData->curLookDir);
		if (_vm->_map->_screenWidth == 640) {
			if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) {
				if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10) {
					if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) == 10)
						animData->nextState = 42;
					else
						animData->nextState = 2;
				} else
					animData->nextState = 40;
			} else if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 20)
				animData->nextState = 38;
			else if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 19)
				animData->nextState = 26;
		}
		break;

	case Map::kDirNE:
		animData->nextState =	3;
		if (_vm->_map->_screenWidth == 640) {
			if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
				animData->nextState = 42;
			if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) != 10)
				animData->nextState = 3;
		}
		break;

	case Map::kDirW:
		animData->nextState = rotateState(0, animData->curLookDir);
		break;

	case Map::kDirE:
		animData->nextState = rotateState(4, animData->curLookDir);
		break;

	case Map::kDirSW:
		animData->nextState = 7;
		if (_vm->_map->_screenWidth == 640) {
			if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
				animData->nextState = 41;
			if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY) != 10)
				animData->nextState = 7;
		}
		break;

	case Map::kDirS:
		animData->nextState = animData->curLookDir == 6 ? 6 : rotateState(6, animData->curLookDir);
		if (_vm->_map->_screenWidth == 640) {
			if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 20)
				animData->nextState = 39;
			else if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 19)
				animData->nextState = 27;
		}
		break;

	case Map::kDirSE:
		animData->nextState = 5;
		if (_vm->_map->_screenWidth == 640) {
			if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
				animData->nextState = 43;
			if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY) != 10)
				animData->nextState = 5;
		}
		break;

	default:
		if (animData->curLookDir == 0)
			animData->nextState = 8;
		else if (animData->curLookDir == 2)
			animData->nextState = 29;
		else if (animData->curLookDir == 4)
			animData->nextState = 9;
		else if (animData->curLookDir == 6)
			animData->nextState = 28;
		break;
	}
}

void Goblin_v2::moveAdvance(Mult::Mult_Object *obj, Gob_Object *gobDesc,
		int16 nextAct, int16 framesCount) {
	Mult::Mult_AnimData *animData;
	int16 gobX;
	int16 gobY;
	int16 animation;
	int16 state;
	int16 layer;

	movePathFind(obj, 0, 0);
	playSounds(obj);

	animData = obj->pAnimData;

	framesCount =
		_vm->_scenery->_animations[(int)animData->animation].layers[animData->layer].framesCount;

	if (animData->isPaused == 0)
		animData->frame++;

	switch (animData->field_16) {
	case 0:
	case 1:
		animData->isPaused = 0;
		break;

	case 4:
		if (animData->frame == 0)
			animData->isPaused = 1;
		break;

	case 6:
		if (animData->frame >= framesCount)
			animData->isPaused = 1;
		break;
	}

	switch(animData->state) {
	case 0:
	case 1:
	case 7:
	case 13:
	case 16:
	case 23:
		// loc_11452
		animData->curLookDir = 0;
		break;

	case 2:
	case 15:
	case 18:
	case 21:
		animData->curLookDir = 2;
		break;

	case 3:
	case 4:
	case 5:
	case 12:
	case 19:
	case 22:
		animData->curLookDir = 4;
		break;

	case 6:
	case 14:
	case 17:
	case 20:
		animData->curLookDir = 6;
		break;

	case 8:
	case 9:
	case 28:
	case 29:
		if (animData->pathExistence == 4)
			animData->pathExistence = 5;
		break;
	}

	if ((animData->field_F != -1) && (animData->frame == framesCount) &&
			(animData->field_F != animData->state)) { // loc_114B6
		animData->nextState = animData->field_F;
		animData->field_F = -1;
		animData->state = animData->nextState;
		*obj->pPosX +=
			_vm->_scenery->_animations[animData->animation].layers[animData->layer].animDeltaX;
		*obj->pPosY +=
			_vm->_scenery->_animations[animData->animation].layers[animData->layer].animDeltaY;
		animation = obj->goblinStates[animData->nextState][0].animation;
		layer = obj->goblinStates[animData->nextState][0].layer;
		animData->layer = layer;
		animData->animation = animation;
		animData->frame = 0;
	} else {
		if (((animData->state >= 0) && (animData->state < 8)) ||
				(animData->state == 38) || (animData->state == 39)) { // loc_115C4
			state = animData->nextState;
			if (animData->frame == ((framesCount + 1) / 2)) {
				gobX = obj->goblinX;
				gobY = obj->goblinY;
				switch (state) {
				case 0:
					obj->goblinX--;
					break;

				case 1:
					obj->goblinX--;
					obj->goblinY--;
					break;

				case 2:
				case 38:
					obj->goblinY--;
					break;

				case 3:
					obj->goblinX++;
					obj->goblinY--;
					break;

				case 4:
					obj->goblinX++;
					break;

				case 5:
					obj->goblinX++;
					obj->goblinY++;
					break;

				case 6:
				case 39:
					obj->goblinY++;
					break;

				case 7:
					obj->goblinX--;
					obj->goblinY++;
					break;
				}
				if (animData->state != state) {
					animation = obj->goblinStates[state][0].animation;
					layer = obj->goblinStates[state][0].layer;
					animData->layer = layer;
					animData->animation = animation;
					animData->frame = 0;
					animData->state = state;
					_vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
					if (_vm->_map->_bigTiles)
						*obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
							(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (gobY + 1) / 2;
					else
						*obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
							(_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
					*obj->pPosX = gobX * _vm->_map->_tilesWidth;
				}
			}
		}

		if (animData->frame >= framesCount) {
			state = animData->nextState;
			animation = obj->goblinStates[state][0].animation;
			layer = obj->goblinStates[state][0].layer;
			animData->layer = layer;
			animData->animation = animation;
			animData->frame = 0;
			animData->state = state;
			gobX = obj->goblinX;
			gobY = obj->goblinY;
			switch (state) {
			case 0:
				obj->goblinX--;
				break;

			case 1:
				obj->goblinX--;
				obj->goblinY--;
				break;

			case 2:
			case 38:
				obj->goblinY--;
				break;

			case 3:
				obj->goblinX++;
				obj->goblinY--;
				break;

			case 4:
				obj->goblinX++;
				break;

			case 5:
				obj->goblinX++;
				obj->goblinY++;
				break;

			case 6:
			case 39:
				obj->goblinY++;
				break;

			case 7:
				obj->goblinX--;
				obj->goblinY++;
				break;
			}
			_vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
			if (_vm->_map->_bigTiles)
				*obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
					(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (gobY + 1) / 2;
			else
				*obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
					(_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
			*obj->pPosX = gobX * _vm->_map->_tilesWidth;
		}
	}
}

} // End of namespace Gob
