/****************************************************************
**
** Attal : Lords of Doom
**
** fightMap.cpp
** Manage the global view
**
** Version : $Id: genericFightMap.cpp,v 1.10 2004/08/01 11:30:19 audoux Exp $
**
** Author(s) : Pascal Audoux - Cyrille Verrier
**
** Date : 02/08/2000
**
** Licence :    
**	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, 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.
**
****************************************************************/

#include "genericFightMap.h"

 
// generic include files
#include <stdlib.h>
// include files for QT
#include <qfile.h>
#include <qstring.h>
#include <qtextstream.h>
// application specific include
#include "libCommon/log.h"
#include "libCommon/pathFinder.h"
#include "libCommon/unit.h"
#include "libCommon/creature.h"

bool isEven( int number )
{
	return( ( number % 2 ) == 0 );
}

bool isOdd( int number )
{
	return( ( number % 2 ) == 1 );
}

//
// ----- GenericFightMap -----
//

GenericFightMap::GenericFightMap()
{
	_height = 0;
	_width = 0;
	
	_genericFightCells = 0;
}

GenericFightMap::~GenericFightMap()
{
       	for( int i=0; i < _height; i++ ) {
		delete [] _genericFightCells[i];
	}
	delete [] _genericFightCells;
}

void GenericFightMap::newFightMap( int h, int w, bool horizontalDraw )
{
	_height = h;
	_width = w;

	_genericFightCells = new GenericFightCell **[_height];

	for( int i = 0; i < _height; i++ ) {
		_genericFightCells[i] = new GenericFightCell * [_width];
		for( int j = 0; j < _width; j++ ) {
			GenericFightCell * cell = new GenericFightCell( i, j );
			_genericFightCells[i][j] = cell;
		}
	}

	_horizontalDraw = horizontalDraw;
}

void GenericFightMap::reinit()
{
	/// XXX: to rewrite... not to use...
	for( int i = 0; i < _height; i++ ) {
		for( int j = 0; j < _width; j++ ) {
			_genericFightCells[i][j]->setUnit( 0 );
		}
	}
}

void GenericFightMap::clearPath()
{
	for( int i = 0; i < _height; i++ ) {
		for( int j = 0; j < _width; j++ ) {
			_genericFightCells[i][j]->setAccess( UNKNOWN_ACCESS );
			_genericFightCells[i][j]->setDist( 0 );
		}
	}
}

void GenericFightMap::initPath( GenericFightUnit * unit )
{
	GenericFightCell * current = unit->getCell();
	FightPile pile( this, unit->getMove(), unit );

	GenericFightMap::clearPath();

	current->setAccess( NONE );
	current->setDist( 0 );

	pile.appendNeighbours( current );
	while( ! pile.isEmpty() ) {
		current = pile.takeSmallest();
		pile.appendNeighbours( current );
	}

	for( int i = 0; i < _height; i++ ) {
		for( int j = 0; j < _width; j++ ) {
			if( _genericFightCells[i][j]->getAccess() == UNKNOWN_ACCESS ) {
				if ( _genericFightCells[i][j]->getUnit() ) {
					_genericFightCells[i][j]->setAccess( FAR_OCCUPIED );
				} else {
					_genericFightCells[i][j]->setAccess( FAR );
				}
			}
		}
	}	
}

void GenericFightMap::printPath()
{
	for( int i = 0; i < _height; i++ ) {
		for( int j = 0; j < _width; j++ ) {
			logDD("%d-%d = %d (%d)", i, j, _genericFightCells[i][j]->getDist(), _genericFightCells[i][j]->getAccess() );
		}
	}		
}

QPtrStack<GenericFightCell> GenericFightMap::computePath( GenericFightUnit *, GenericFightCell * cell )
{
	QPtrStack<GenericFightCell> path;
	int dist = cell->getDist();
	GenericFightCell * current = cell;
	
	path.push( cell );
	for( int i = dist - 1; i > 0; i-- ) {
		current = giveNeighbourOnPath( current, i );
		path.push( current );
	}
	//path.push( unit->getCell() );
	
	return path;
	
}

GenericFightCell * GenericFightMap::giveNeighbourOnPath( GenericFightCell * cell, int dist )
{
	GenericFightCell * current;
	GenericFightCell * ret = 0;
	
	current = getNeighbour1( cell );
	if( current && ( current->getDist() == dist )
		&& ( current->getAccess () != NONE )
		&& ( current->getAccess () != NEAR_OCCUPIED ) ) {
		ret = current;
	}
	current = getNeighbour2( cell );
	if( current && ( current->getDist() == dist )
		&& ( current->getAccess () != NONE )
		&& ( current->getAccess () != NEAR_OCCUPIED ) ) {
		ret = current;
	}
	current = getNeighbour3( cell );
	if( current && ( current->getDist() == dist )
		&& ( current->getAccess () != NONE )
		&& ( current->getAccess () != NEAR_OCCUPIED ) ) {
		ret = current;
	}
	current = getNeighbour4( cell );
	if( current && ( current->getDist() == dist )
		&& ( current->getAccess () != NONE )
		&& ( current->getAccess () != NEAR_OCCUPIED ) ) {
		ret = current;
	}
	current = getNeighbour5( cell );
	if( current && ( current->getDist() == dist )
		&& ( current->getAccess () != NONE )
		&& ( current->getAccess () != NEAR_OCCUPIED ) ) {
		ret = current;
	}
	current = getNeighbour6( cell );
	if( current && ( current->getDist() == dist )
		&& ( current->getAccess () != NONE )
		&& ( current->getAccess () != NEAR_OCCUPIED ) ) {
		ret = current;
	}

	if( ! ret ) {
		logEE( "Should not happen : no cell found ??" );
	}

	return ret;
}

GenericFightCell * GenericFightMap::getNeighbour1( GenericFightCell * cell )
{
	int row = cell->getRow();
	int col = cell->getCol();
	GenericFightCell * ret = 0;

	if ( _horizontalDraw )
	{
		if( row > 0 ) {
			ret = at( row - 1, col );
		}
	}
	else
	{
		if ( !isEven ( row ) )
		{
			if (( row > 0 ) && ( col < _width - 1 ))
			{
				ret = at ( row - 1, col + 1 );
			}
		}
		else if ( row > 0 )
		{
			ret = at ( row - 1, col );
		}
	}

	return ret;
}

GenericFightCell * GenericFightMap::getNeighbour2( GenericFightCell * cell )
{
	int row = cell->getRow();
	int col = cell->getCol();
	GenericFightCell * ret = 0;

	if ( _horizontalDraw )
	{
		if( isEven( col ) ) {
			if( ( row > 0 ) && ( col < _width-1 ) ){
				ret = at( row - 1, col + 1 );
			}
		} else {
			if( col < _width-1 ) {
				ret = at( row, col + 1 );
			}
		}
	}
	else
	{
		if ( col < _width - 1)
		{
			ret = at ( row , col + 1 );
		}
	}

	return ret;
}

GenericFightCell * GenericFightMap::getNeighbour3( GenericFightCell * cell )
{
	int row = cell->getRow();
	int col = cell->getCol();
	GenericFightCell * ret = 0;

	if ( _horizontalDraw )
	{
		if( isEven( col ) ) {
			if( col < _width - 1 ) {
				ret = at( row, col + 1 );
			}
		} else {
			if( ( col < _width - 1 ) && ( row < _height - 1 ) ) {
				ret = at( row + 1, col + 1 );
			}
		}
	}
	else
	{
		if ( !isEven ( row ) )
		{
			if (( row < _height - 1 ) && ( col < _width - 1 ))
			{
				ret = at ( row + 1, col + 1 );
			}
		}
		else if ( row < _height - 1 )
		{
			ret = at ( row + 1, col );
		}
	}

	return ret;
}

GenericFightCell * GenericFightMap::getNeighbour4( GenericFightCell * cell )
{
	int row = cell->getRow();
	int col = cell->getCol();
	GenericFightCell * ret = 0;

	if ( _horizontalDraw )
	{
		if( row < _height - 1 ) {
			ret = at( row + 1, col );
		}
	}
	else
	{
		if ( isEven ( row ) )
		{
			if (( row < _height - 1 ) && ( col > 0 ))
			{
				ret = at ( row + 1, col - 1 );
			}
		}
		else if ( row < _height - 1 )
		{
			ret = at ( row + 1, col );
		}
	}

	return ret;
}

GenericFightCell * GenericFightMap::getNeighbour5( GenericFightCell * cell )
{
	int row = cell->getRow();
	int col = cell->getCol();
	GenericFightCell * ret = 0;

	if ( _horizontalDraw )
	{
		if( isEven( col ) ) {
			if( col > 0 ) {
				ret = at( row, col - 1 );
			}
		} else {
			if( ( row < _height - 1 ) && ( col > 0 ) ) {
				ret = at( row + 1, col - 1 );
			}
		}
	}
	else
	{
		if ( col > 0 )
		{
			ret = at ( row , col - 1);
		}
	}

	return ret;
}

GenericFightCell * GenericFightMap::getNeighbour6( GenericFightCell * cell )
{
	int row = cell->getRow();
	int col = cell->getCol();
	GenericFightCell * ret = 0;

	if ( _horizontalDraw )
	{
		if( isEven( col ) ) {
			if( ( row > 0 ) && ( col > 0 ) ) {
				ret = at( row - 1, col - 1 );
			}
		} else {
			if( col > 0 ) {
				ret = at( row, col - 1 );
			}
		}
	}
	else
	{
		if ( isEven ( row ) )
		{
			if (( row > 0 ) && ( col > 0 ))
			{
				ret = at ( row - 1, col - 1 );
			}
		}
		else if ( row > 0 )
		{
			ret = at ( row - 1, col );
		}
	}

	return ret;
}

bool GenericFightMap::areNeighbours( GenericFightCell * cell1, GenericFightCell * cell2 )
{
	bool ret = false;

	if( ( getNeighbour1( cell1 ) == cell2 ) ||
	    ( getNeighbour2( cell1 ) == cell2 ) ||
	    ( getNeighbour3( cell1 ) == cell2 ) ||
	    ( getNeighbour4( cell1 ) == cell2 ) ||
	    ( getNeighbour5( cell1 ) == cell2 ) ||
	    ( getNeighbour6( cell1 ) == cell2 ) ) {
		ret = true;
	}

	return ret;
}

/*********************************************/
FightPile::FightPile( GenericFightMap * map, int limit,	GenericFightUnit* unit )
{
	_map = map;
	_limit = limit;
	_unit = unit;
}

GenericFightCell * FightPile::takeSmallest()
{
	GenericFightCell * smallest = first();
	int minimum = at();
	GenericFightCell * current = next();

	while( current ) {
		if( smallest->getDist() > current->getDist() ) {
			smallest = current;
			minimum = at();
		}
		current = next();
	}

	return take( minimum );
}

void FightPile::appendNeighbours( GenericFightCell * cell )
{
	int dist = cell->getDist() + 1;

	handleNeighbour( _map->getNeighbour1( cell ), dist );
	handleNeighbour( _map->getNeighbour2( cell ), dist );
	handleNeighbour( _map->getNeighbour3( cell ), dist );
	handleNeighbour( _map->getNeighbour4( cell ), dist );
	handleNeighbour( _map->getNeighbour5( cell ), dist );
	handleNeighbour( _map->getNeighbour6( cell ), dist );
}

void FightPile::handleNeighbour( GenericFightCell * neighbour, int dist )
{
	if( neighbour ) {

		if( neighbour->getType() == OBSTACLE ) {
			neighbour->setAccess( NONE );
		}

		bool isHeadFreeToGo = testHeadFree( neighbour );

		bool isOccupiedByAnotherUnit = false;

		if ( neighbour->getUnit ()  && ( neighbour->getUnit ()->isAlive () ) )
		{
			isOccupiedByAnotherUnit = ( neighbour->getUnit () != _unit );
		}

		switch( neighbour->getAccess() ) {
		case UNKNOWN_ACCESS:
			if( dist <= _limit ) {
				if( isOccupiedByAnotherUnit || ( !isHeadFreeToGo )) {
					neighbour->setAccess( NEAR_OCCUPIED );
					neighbour->setDist( dist );
				} else {
					neighbour->setAccess( NEAR );
					neighbour->setDist( dist );
					append( neighbour );
				}
			} else if( isOccupiedByAnotherUnit || ( !isHeadFreeToGo ) ) {
				neighbour->setAccess( FAR_OCCUPIED );
				neighbour->setDist( dist );
			} else {
				neighbour->setAccess( FAR );
				neighbour->setDist( dist );
			}
			break;
		case NONE:
			break;
		case NEAR:
			if( dist < neighbour->getDist() ) {
				neighbour->setDist( dist );
				append( neighbour );
			}
			break;
		case NEAR_OCCUPIED:
			if( dist < neighbour->getDist() ) {
				neighbour->setDist( dist );
			}
			break;
		case FAR:
			if( dist < neighbour->getDist() ) {
				neighbour->setDist( dist );
			}
			if( neighbour->getDist() <= _limit ) {
				neighbour->setAccess( NEAR );
				append( neighbour );
			}
			break;
		case FAR_OCCUPIED:
			if( dist < neighbour->getDist() ) {
				neighbour->setDist( dist );
			}
			if( neighbour->getDist() <= _limit ) {
				neighbour->setAccess( NEAR_OCCUPIED );
			}
			break;
		default:
			logEE( "Should not happen" );
			break;
		}
	}
}

bool FightPile::testHeadFree( GenericFightCell * cell )
{
	bool ret = true;

	// If unit is 2-cells we can have problems with its head

	if( _unit->getCreature()->getSize() == 2 ) {
		GenericFightCell* headCell = 0;
		if( _unit->isLookingToRight() ) {
			headCell = _map->getNeighbour2( cell );
		} else	{
			headCell = _map->getNeighbour5( cell );
		}

		if( headCell )	{
			if ( headCell->getType () == OBSTACLE )	{
				cell->setAccess ( NONE );
			} else	{
				if( ( headCell->getUnit () ) &&
				( headCell->getUnit () != _unit )
				 && ( headCell->getUnit ()->isAlive () ) ) {
					ret = false;
				}
			}
		} else {
			cell->setAccess( NONE );
		}
	}

	return ret;
}
