/* 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/fight/fighter.h"

#include "lastexpress/data/sequence.h"

#include "lastexpress/game/scenes.h"

#include "lastexpress/sound/sound.h"

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

namespace LastExpress {

Fighter::Fighter(LastExpressEngine *engine) : _engine(engine) {
	_opponent = NULL;
	_fight = NULL;

	_sequenceIndex = 0;
	_sequence = NULL;
	_frame = NULL;
	_frameIndex = 0;

	_field_24 = 0;

	_action = kFightAction101;
	_sequenceIndex2 = 0;

	_countdown = 1;

	_field_34 = 0;
}

Fighter::~Fighter() {
	// The original game resets the function pointers to default values, just before deleting the struct

	getScenes()->removeAndRedraw(&_frame, false);

	// Free sequences
	for (uint i = 0; i < _sequences.size(); i++)
		SAFE_DELETE(_sequences[i]);

	// Zero-out passed pointers
	_sequence = NULL;
	_opponent = NULL;
	_fight = NULL;

	_engine = NULL;
}

//////////////////////////////////////////////////////////////////////////
// Drawing
//////////////////////////////////////////////////////////////////////////
void Fighter::setSequenceAndDraw(uint32 sequenceIndex, FightSequenceType type) {
	if (_sequences.size() < sequenceIndex)
		return;

	switch (type) {
	default:
		break;

	case kFightSequenceType0:
		if (_sequenceIndex)
			return;

		_sequence = _sequences[sequenceIndex];
		_sequenceIndex = sequenceIndex;
		draw();
		break;

	case kFightSequenceType1:
		_sequence = _sequences[sequenceIndex];
		_sequenceIndex = sequenceIndex;
		_sequenceIndex2 = 0;
		draw();
		break;

	case kFightSequenceType2:
		_sequenceIndex2 = sequenceIndex;
		break;
	}
}

void Fighter::draw() {
	getScenes()->removeAndRedraw(&_frame, false);

	_frameIndex = 0;
	_field_24 = 0;
}

//////////////////////////////////////////////////////////////////////////
// Processing
//////////////////////////////////////////////////////////////////////////
void Fighter::process() {
	if (!_fight)
		error("[Fighter::handleAction] Fighter not initialized properly");

	if (!_sequence) {
		if (_frame) {
			getScenes()->removeFromQueue(_frame);
			getScenes()->setCoordinates(_frame);
		}
		SAFE_DELETE(_frame);
		return;
	}

	if (_sequence->count() <= _frameIndex) {
		switch(_action) {
		default:
			break;

		case kFightAction101:
			setSequenceAndDraw(_sequenceIndex2, kFightSequenceType1);
			_sequenceIndex2 = 0;
			break;

		case kFightActionResetFrame:
			_frameIndex = 0;
			break;

		case kFightAction103:
			setSequenceAndDraw(0, kFightSequenceType1);
			handleAction(kFightAction101);
			_opponent->setSequenceAndDraw(0, kFightSequenceType1);
			_opponent->handleAction(kFightAction101);
			_opponent->update();
			break;

		case kFightActionWin:
			_fight->bailout(Fight::kFightEndWin);
			break;

		case kFightActionLost:
			_fight->bailout(Fight::kFightEndLost);
			break;
		}
	}

	if (_fight->isRunning()) {

		// Get the current sequence frame
		SequenceFrame *frame = new SequenceFrame(_sequence, (uint16)_frameIndex);
		frame->getInfo()->location = 1;

		if (_frame == frame) {
			delete frame;
			return;
		}

		getSound()->playFightSound(frame->getInfo()->soundAction, frame->getInfo()->field_31);

		// Add current frame to queue and advance
		getScenes()->addToQueue(frame);
		_frameIndex++;

		if (_frame) {
			getScenes()->removeFromQueue(_frame);

			if (!frame->getInfo()->field_2E)
				getScenes()->setCoordinates(_frame);
		}

		// Replace by new frame
		delete _frame;
		_frame = frame;
	}
}

//////////////////////////////////////////////////////////////////////////
// Default actions
//////////////////////////////////////////////////////////////////////////
void Fighter::handleAction(FightAction action) {
	if (!_opponent || !_fight)
		error("[Fighter::handleAction] Fighter not initialized properly");

	switch (action) {
	default:
		return;

	case kFightAction101:
		break;

	case kFightActionResetFrame:
		_countdown--;
		break;

	case kFightAction103:
		_opponent->handleAction(kFightActionResetFrame);
		break;

	case kFightActionWin:
		_fight->setEndType(Fight::kFightEndWin);
		_opponent->handleAction(kFightActionResetFrame);
		break;

	case kFightActionLost:
		_fight->setEndType(Fight::kFightEndLost);
		_opponent->handleAction(kFightActionResetFrame);
		break;
	}

	// Update action
	_action = action;
}

bool Fighter::canInteract(FightAction /*action = kFightActionNone*/ ) {
	return (_action == kFightAction101 && !_sequenceIndex);
}

void Fighter::update() {
	process();

	if (_frame)
		_frame->getInfo()->location = (_action == kFightActionResetFrame ? 2 : 0);
}

void Opponent::update() {
	process();

	if (_field_38 && !_sequenceIndex)
		_field_38--;

	if (_frame)
		_frame->getInfo()->location = 1;
}

//////////////////////////////////////////////////////////////////////////
// Helpers
//////////////////////////////////////////////////////////////////////////
bool Fighter::checkFrame(uint32 val) {
	if (!_frame)
		error("[Fighter::checkFrame] Invalid current frame");

	return (bool)(_frame->getInfo()->field_33 & val);
}

} // End of namespace LastExpress