/* 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.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-11-0/engines/kyra/animator_v2.cpp $
 * $Id: animator_v2.cpp 30459 2008-01-12 23:07:51Z sev $
 *
 */

#include "kyra/kyra_v2.h"
#include "kyra/wsamovie.h"

namespace Kyra {

void KyraEngine_v2::clearAnimObjects() {
	memset(_animObjects, 0, sizeof(_animObjects));
	
	_animObjects[0].index = 0;
	_animObjects[0].type = 0;
	_animObjects[0].enabled = 1;
	_animObjects[0].flags = 0x800;
	_animObjects[0].width = 32;
	_animObjects[0].height = 49;
	_animObjects[0].width2 = 4;
	_animObjects[0].height2 = 10;
	
	for (int i = 1; i < 11; ++i) {
		_animObjects[i].index = i;
		_animObjects[i].type = 2;
	}
	
	for (int i = 11; i <= 40; ++i) {
		_animObjects[i].index = i;
		_animObjects[i].type = 1;
		_animObjects[i].flags = 0x800;
		_animObjects[i].width = 16;
		_animObjects[i].height = 16;
	}
}

KyraEngine_v2::AnimObj *KyraEngine_v2::initAnimList(AnimObj *list, AnimObj *entry) {
	entry->nextObject = list;
	return entry;
}

KyraEngine_v2::AnimObj *KyraEngine_v2::addToAnimListSorted(AnimObj *list, AnimObj *add) {
	if (add->yPos1 <= list->yPos1) {
		add->nextObject = list;
		return add;
	}

	AnimObj *cur = list;
	AnimObj *prev = list;
	while (add->yPos1 > cur->yPos1) {
		AnimObj *temp = cur->nextObject;
		if (!temp)
			break;
		prev = cur;
		cur = temp;
	}
	
	if (add->yPos1 <= cur->yPos1) {
		prev->nextObject = add;
		add->nextObject = cur;
	} else {
		cur->nextObject = add;
		add->nextObject = 0;
	}
	return list;
}

KyraEngine_v2::AnimObj *KyraEngine_v2::deleteAnimListEntry(AnimObj *list, AnimObj *entry) {
	if (!list)
		return 0;

	AnimObj *old = 0;
	AnimObj *cur = list;

	while (true) {
		if (cur == entry)
			break;
		if (!cur->nextObject)
			break;
		old = cur;
		cur = cur->nextObject;
	}

	if (cur == list) {
		if (!cur->nextObject)
			return 0;
		cur = cur->nextObject;
		return cur;
	}

	if (!cur->nextObject) {
		if (!old)
			return 0;
		old->nextObject = 0;
		return list;
	}

	if (cur != entry)
		return list;

	old->nextObject = entry->nextObject;
	return list;
}

void KyraEngine_v2::drawAnimObjects() {
	for (AnimObj *curObject = _animList; curObject; curObject = curObject->nextObject) {
		if (!curObject->enabled)
			continue;
		
		int x = curObject->xPos2 - (_screen->getScreenDim(2)->sx << 3);
		int y = curObject->yPos2 - _screen->getScreenDim(2)->sy;
		int layer = 7;
		
		if (curObject->flags & 0x800) {
			if (curObject->animFlags)
				layer = 0;
			else
				layer = getDrawLayer(curObject->xPos1, curObject->yPos1);
		}
		curObject->flags |= 0x800;

		if (curObject->index)
			drawSceneAnimObject(curObject, x, y, layer);
		else
			drawCharacterAnimObject(curObject, x, y, layer);
	}
}

void KyraEngine_v2::refreshAnimObjects(int force) {
	for (AnimObj *curObject = _animList; curObject; curObject = curObject->nextObject) {
		if (!curObject->enabled)
			continue;
		if (!curObject->needRefresh && !force)
			continue;

		int x = curObject->xPos2 - curObject->width2;
		if (x < 0)
			x = 0;
		if (x >= 320)
			x = 319;
		int y = curObject->yPos2 - curObject->height2;
		if (y < 0)
			y = 0;
		if (y >= 143)
			y = 142;

		int width = curObject->width + curObject->width2 + 8;
		int height = curObject->height + curObject->height2*2;
		if (width + x > 320)
			width -= width + x - 322;
		if (height + y > 143)
			height -= height + y - 144;
	
		_screen->hideMouse();
		_screen->copyRegion(x, y, x, y, width, height, 2, 0, Screen::CR_CLIPPED);
		_screen->showMouse();
		
		curObject->needRefresh = false;
	}
}

void KyraEngine_v2::refreshAnimObjectsIfNeed() {
	for (AnimObj *curEntry = _animList; curEntry; curEntry = curEntry->nextObject) {
		if (curEntry->enabled && curEntry->needRefresh) {
			restorePage3();
			drawAnimObjects();
			refreshAnimObjects(0);
			_screen->updateScreen();
			return;
		}
	}
}

void KyraEngine_v2::flagAnimObjsForRefresh() {
	for (AnimObj *curEntry = _animList; curEntry; curEntry = curEntry->nextObject)
		curEntry->needRefresh = 1;
}

void KyraEngine_v2::updateCharFacing() {
	if (_mainCharacter.x1 > _mouseX)
		_mainCharacter.facing = 5;
	else
		_mainCharacter.facing = 3;

	_mainCharacter.animFrame = _characterFrameTable[_mainCharacter.animFrame];
	updateCharacterAnim(0);
	refreshAnimObjectsIfNeed();
}

void KyraEngine_v2::updateCharacterAnim(int) {
	Character *c = &_mainCharacter;
	AnimObj *animState = _animObjects;

	animState->needRefresh = 1;
	animState->unk8 = 1;

	if (c->facing >= 1 && c->facing <= 3)
		animState->flags |= 1;
	else if (c->facing >= 5 && c->facing <= 7)
		animState->flags &= ~1;

	animState->xPos2 = animState->xPos1 = c->x1;
	animState->yPos2 = animState->yPos1 = c->y1;
	animState->shapePtr = _defaultShapeTable[c->animFrame];
	animState->shapeIndex1 = animState->shapeIndex2 = c->animFrame;

	int xAdd = _shapeDescTable[c->animFrame-9].xAdd;
	int yAdd = _shapeDescTable[c->animFrame-9].yAdd;

	_charScaleX = _charScaleY = getScale(c->x1, c->y1);

	animState->xPos2 += (xAdd * _charScaleX) >> 8;
	animState->yPos2 += (yAdd * _charScaleY) >> 8;
	animState->width2 = 8;
	animState->height2 = 10;

	_animList = deleteAnimListEntry(_animList, animState);
	if (_animList)
		_animList = addToAnimListSorted(_animList, animState);
	else
		_animList = initAnimList(_animList, animState);

	updateCharPal(1);
}

void KyraEngine_v2::updateSceneAnim(int anim, int newFrame) {
	AnimObj *animObject = &_animObjects[1+anim];
	if (!animObject->enabled)
		return;
	
	animObject->needRefresh = 1;
	animObject->unk8 = 1;
	animObject->flags = 0;
	
	if (_sceneAnims[anim].flags & 2)
		animObject->flags |= 0x800;
	else
		animObject->flags &= ~0x800;
	
	if (_sceneAnims[anim].flags & 4)
		animObject->flags |= 1;
	else
		animObject->flags &= ~1;
	
	if (_sceneAnims[anim].flags & 0x20) {
		animObject->shapePtr = _sceneShapeTable[newFrame];
		animObject->shapeIndex2 = 0xFFFF;
		animObject->shapeIndex3 = 0xFFFF;
		animObject->animNum = 0xFFFF;		
	} else {
		animObject->shapePtr = 0;
		animObject->shapeIndex3 = newFrame;
		animObject->animNum = anim;
	}
	
	animObject->xPos1 = _sceneAnims[anim].x;
	animObject->yPos1 = _sceneAnims[anim].y;
	animObject->xPos2 = _sceneAnims[anim].x2;
	animObject->yPos2 = _sceneAnims[anim].y2;
	
	if (_sceneAnims[anim].flags & 2) {
		_animList = deleteAnimListEntry(_animList, animObject);
		if (!_animList)
			_animList = initAnimList(_animList, animObject);
		else
			_animList = addToAnimListSorted(_animList, animObject);
	}
}

void KyraEngine_v2::drawSceneAnimObject(AnimObj *obj, int x, int y, int layer) {
	if (obj->type == 1) {
		if (obj->shapeIndex1 == 0xFFFF)
			return;
		int scale = getScale(obj->xPos1, obj->yPos1);
		_screen->drawShape(2, getShapePtr(obj->shapeIndex1), x, y, 2, obj->flags | 4, layer, scale, scale);
		return;
	}

	if (obj->shapePtr) {
		_screen->drawShape(2, obj->shapePtr, x, y, 2, obj->flags, layer);
	} else {
		if (obj->shapeIndex3 == 0xFFFF || obj->animNum == 0xFFFF)
			return;

		int flags = 0x4000;
		if (obj->flags & 0x800)
			flags |= 0x8000;

		if (_sceneAnims[obj->animNum].wsaFlag) {
			x = y = 0;
		} else {
			x = obj->xPos2;
			y = obj->yPos2;
		}

		_sceneAnimMovie[obj->animNum]->setX(x);
		_sceneAnimMovie[obj->animNum]->setY(y);
		_sceneAnimMovie[obj->animNum]->setDrawPage(2);
		_sceneAnimMovie[obj->animNum]->displayFrame(obj->shapeIndex3, int(flags | layer), 0, 0);
	}
}

void KyraEngine_v2::drawCharacterAnimObject(AnimObj *obj, int x, int y, int layer) {
	if (_drawNoShapeFlag || obj->shapeIndex1 == 0xFFFF)
		return;
	_screen->drawShape(2, getShapePtr(obj->shapeIndex1), x, y, 2, obj->flags | 4, layer, _charScaleX, _charScaleY);
}

void KyraEngine_v2::addItemToAnimList(int item) {
	restorePage3();

	AnimObj *animObj = &_animObjects[11+item];

	animObj->enabled = 1;
	animObj->needRefresh = 1;
	animObj->unk8 = 1;

	int itemId = _itemList[item].id;

	animObj->xPos2 = animObj->xPos1 = _itemList[item].x;
	animObj->yPos2 = animObj->yPos1 = _itemList[item].y;

	animObj->shapePtr = _defaultShapeTable[64+itemId];
	animObj->shapeIndex2 = animObj->shapeIndex1 = 64+itemId;

	int scaleY, scaleX;
	scaleY = scaleX = getScale(animObj->xPos1, animObj->yPos1);

	uint8 *shapePtr = getShapePtr(64+itemId);
	animObj->xPos3 = (animObj->xPos2 -= (_screen->getShapeScaledWidth(shapePtr, scaleX) >> 1));
	animObj->yPos3 = (animObj->yPos2 -= _screen->getShapeScaledHeight(shapePtr, scaleY));

	animObj->width2 = animObj->height2 = 0;

	_animList = addToAnimListSorted(_animList, animObj);
	animObj->needRefresh = 1;
	animObj->unk8 = 1;
}

void KyraEngine_v2::deleteItemAnimEntry(int item) {
	AnimObj *animObj = &_animObjects[11+item];

	restorePage3();

	animObj->shapePtr = 0;
	animObj->shapeIndex1 = 0xFFFF;
	animObj->shapeIndex2 = 0xFFFF;
	animObj->needRefresh = 1;
	animObj->unk8 = 1;

	refreshAnimObjectsIfNeed();

	animObj->enabled = 0;
	_animList = deleteAnimListEntry(_animList, animObj);
}

void KyraEngine_v2::setCharacterAnimDim(int w, int h) {
	restorePage3();

	_animObj0Width = _animObjects[0].width;
	_animObj0Height = _animObjects[0].height;

	_animObjects[0].width = w;
	_animObjects[0].height = h;
}

void KyraEngine_v2::resetCharacterAnimDim() {
	restorePage3();

	_animObjects[0].width = _animObj0Width;
	_animObjects[0].height = _animObj0Height;
}

} // end of namespace Kyra

