/****************************************************************
**
** Attal : Lords of Doom
**
** analyst.cpp
** analyse the game and play
**
** Version : $Id: analyst.cpp,v 1.53 2004/12/27 22:52:45 audoux Exp $
**
** Author(s) : Pascal Audoux - Sardi Carlo
**
** Date : 06/02/2001
**
** 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 "analyst.h"

// generic include files
#include <stdlib.h>
// include files for QT
// application specific include files
#include "libCommon/artefactManager.h"
#include "libCommon/attalSocket.h"
#include "libCommon/calendar.h"
#include "libCommon/dataTheme.h"
#include "libCommon/genericBase.h"
#include "libCommon/genericBonus.h"
#include "libCommon/genericBuilding.h"
#include "libCommon/genericChest.h"
#include "libCommon/genericLord.h"
#include "libCommon/genericMap.h"
#include "libCommon/genericEvent.h"
#include "libCommon/genericMapCreature.h"
#include "libCommon/genericPlayer.h"
#include "libCommon/pathFinder.h"
#include "libCommon/priceMarket.h"

#include "libServer/fightAnalyst.h"

#include "ai/aiInterface.h"


extern DataTheme DataTheme;

//
// ----- Analyst -----
//

Analyst::Analyst()
: GameData()
{
	_fight = 0;
	_socket = 0;
	_calendar = new Calendar();
	_calendar->reinit();

	_map = new GenericMap();
	_player = new GenericPlayer( _map );

	/*_lords = new GenericLord * [ DataTheme.lords.count() ];
	for( uint i = 0; i < DataTheme.lords.count(); i++ ) {
		_lords[i] = new GenericLord();
		_lords[i]->setId( i );
	}*/
	_numTurn=-1;
	_numlords=0;
	for(int i = PR_ENEMY;i<=PR_LAST;i++){ 
		_priorities[i]=i;
	}	
	srand( time( NULL ) );
	_SuffisantPower = 1000;
}

Analyst::~Analyst()
{
	delete _player;
	delete _map;
	delete _calendar;
}

void Analyst::reinitAi()
{
	reinit();

	//XXX : cause bug, but to use when socket api will change
	//delete _player;
	//delete _map;
	//delete _calendar;
	
	_fight = 0;
	//_calendar = new Calendar();
	_calendar->reinit();

	//_map = new GenericMap();
	//_player = new GenericPlayer( _map );

	/*_lords = new GenericLord * [ DataTheme.lords.count() ];
	for( uint i = 0; i < DataTheme.lords.count(); i++ ) {
		_lords[i] = new GenericLord();
		_lords[i]->setId( i );
	}*/
	_player->cleanData();
	_map->cleanData();
	_numTurn=-1;
	_numlords=0;

}


/*
GenericLord * Analyst::getLord( uint num )
{
	GenericLord * lord = 0;

	if( num < DataTheme.lords.count() ) {
		lord = _lords[num];
	}

	return lord;
}
*/
void Analyst::socketMsg()
{
	ialogV( "SO_MSG" );
}


/*!

*/

void Analyst::socketConnect()
{
	ialogV( "SO_CONNECT" );
	switch( _socket->getCla2() ) {
	case C_CONN_OK:
		ialogN( "Receive: Connect Ok" );
		_socket->sendConnectionName( "IA" );
		break;
	case C_CONN_ID:{
		_player->setNum(_socket->readChar());
		ialogN( "Receive: Connect Id = %d", _player->getNum() );
		}
		break;
	case C_CONN_NAME:	
		{
			QString res;
			uint len = _socket->readChar();
			for( uint i = 0; i < len; i++) {
				res.append( _socket->readChar() );
			}
			_player->setName(res);
			ialogN( "Receive: Connect Name %s", res.latin1());
		}
		break;
	case C_CONN_PLAYER:
		ialogN( "Receive: Connect Player" );
		break;
	}
}

void Analyst::socketMvt()
{
	ialogV( "SO_MVT" );
	uchar lord = _socket->readChar();
	int row = _socket->readInt();
	int col = _socket->readInt();

	GenericLord * theLord = getLord( lord );

	int cost = theLord->computeCostMvt( _map->at( row, col ) );
	if( cost >= 0 ) {
		theLord->decreaseBaseCharac( MOVE, cost );
	}

	theLord->moveTo( _map->at( row, col ) );

	if( _map->computeMinimalNextCost( theLord ) > theLord->getCharac( MOVE ) ) {
		theLord->setBaseCharac( MOVE, 0 );
	}

	/// TODO: manage enter inside buildings and bases
	if( _map->at( row, col )->getBuilding() != 0 ) {
			if( _map->at(row, col)->getBuilding()->getOwner() != theLord->getOwner()) {
			 _map->at(row, col)->getBuilding()->setOwner(theLord->getOwner()); 
			}	
		if( theLord->getOwner() == _player ) {
			/// TODO: enter( _player->getSelectedLord(), _map->at( row, col )->getBuilding() );
		}
	} else if( _map->at( row, col )->getBase() != 0 ) {
		if( theLord->getOwner() == _player ) {
			enterBase(theLord, _map->at( row, col )->getBase());
		}
	}
}


void Analyst::socketTechnic()
{
	ialogV( "SO_TECHNIC" );
}

void Analyst::socketFight()
{
	ialogV( "SO_FIGHT" );

	switch( _socket->getCla2() ) {
	case C_FIGHT_INIT:
		ialogN( "FIGHT INIT" );
		if( ! _fight ) {
			_fight = new FightAnalyst( this );
			_fight->setSocket( _socket );
		}
		_fight->handleFightSocket();
		break;
	case C_FIGHT_END:
		ialogN( "FIGHT END" );
		//logDD( "ai: FIGHT END" );
		bool isCrea;
		isCrea	= _fight->IsCreature(); 
		_fight->updateUnits();
		delete _fight;
		_fight = 0;
		if(InTurn() && isCrea){ 
		ialogV( "FIGHT END creature" );
			_socket->sendLordTurn( PL_SAME_LORD );
		}
		break;
	default:
		if( _fight ) {
			_fight->handleFightSocket();
		} else {
			logEE( "Should not happen" );
		}
		break;
	}
}


/*!

*/

void Analyst::socketQR()
{
	ialogV( "SO_QR" );
	switch( _socket->getCla2() ) {
	case C_QR_MSG_NEXT: 
		break;
	case C_QR_MSG_END:
		/// XXX: maybe we can display in log the msg...
		break;
	case C_QR_LEVEL: {
		ialogV( "level" );
		//_socket->sendAnswer( 0 );
		} break;
	case C_QR_CHEST: {
		ialogV( "Chest" );
		_socket->sendAnswerEnum( 0 );
		_socket->sendLordTurn( PL_SAME_LORD );
		} break;
	case C_QR_CREATURE_FLEE: 
		_socket->sendAnswerYesNo( true );
		_socket->sendLordTurn( PL_SAME_LORD );
		break;
	case C_QR_CREATURE_MERCENARY:
		_socket->sendAnswerYesNo( false );
		_socket->sendLordTurn( PL_SAME_LORD );
		break;
	case C_QR_CREATURE_JOIN: 
		_socket->sendAnswerYesNo( true );
		_socket->sendLordTurn( PL_SAME_LORD );
		break;
	case C_QR_ANSWER:
		logEE( "Should not happen" );
		break;
	}

}

void Analyst::socketExch()
{
	ialogV( "SO_EXCH" );

	switch( _socket->getCla2() ) {
	case C_EXCH_START:
		exchangeStart();
		break;
	case C_EXCH_UNIT:
	  	exchangeUnits();
		break;
	case C_EXCH_ARTEFACT:
		exchangeArtefact();
		break;
	case C_EXCH_BASEUNITCL:
	  	exchangeBaseUnits();
		break;
	default:
		break;
	}
}

void Analyst::exchangeStart()
{
	/// XXX: not finished...
	//uchar idLord1 = _socket->readChar();
	//uchar idLord2 = _socket->readChar();
	//GenericLord * lord1 = _lords.at( idLord1 );
	//GenericLord * lord2 = _lords.at( idLord2 );
}
void Analyst::exchangeUnits()
{
	uchar idLord1 = _socket->readChar();
	uchar idUnit1 = _socket->readChar();
	uchar idLord2 = _socket->readChar();
	uchar idUnit2 = _socket->readChar();
	GenericLord * lord1 = 0;
	GenericLord * lord2 = 0;
	if( idLord1 ) {
		lord1 = _lords.at( idLord1 );
	}
	if( idLord2 ) {
		lord2 = _lords.at( idLord2 );
	}

	/// XXX: check player of lord ?
	if( lord1 && lord2 ) {
		GenericFightUnit * unit1 = lord1->getUnit( idUnit1 );
		GenericFightUnit * unit2 = lord2->getUnit( idUnit2 );

		if( unit1 ) {
			if( unit2 ) {
				if( ( unit1->getRace() == unit2->getRace() ) &&
					unit1->getLevel() == unit2->getLevel() ) {
					unit2->addNumber( unit1->getNumber() );
					lord1->setUnit( idUnit1, 0 );
					delete unit1;
				} else {
					lord1->setUnit( idUnit1, unit2 );
					lord2->setUnit( idUnit2, unit1 );
				}
			} else {
				lord2->setUnit( idUnit2, unit1 );
				lord1->setUnit( idUnit1, 0 );
			}
		}
	}
}

void Analyst::exchangeArtefact()
{
	ialogC( "Exchange artefact not implemented" );
}

void Analyst::exchangeBaseUnits()
{
	int row = _socket->readInt();
	int col = _socket->readInt();
	uchar idUnit1 = _socket->readChar();
	uchar idLord = _socket->readChar();
	uchar idUnit2 = _socket->readChar();
	GenericBase * base = 0;
	GenericLord * lord = 0;
	GenericFightUnit * uni1 = 0;
	GenericFightUnit * uni2 = 0;

	base = _map->at( row, col )->getBase();
	if( idLord && ( idLord < 255 ) ) {
		lord = _lords.at( idLord );
	}

	if( base ) {
		if( idUnit1 <= MAX_UNIT ){
			uni1 = base->getUnit( idUnit1 );
		}
		if( lord ) {
			if( idUnit2 <= MAX_UNIT ){
				uni2 = lord->getUnit( idUnit2 );
			}
			if( uni1 && uni2 ) {
				if( uni1->getCreature() != uni2->getCreature() ) {
					lord->setUnit( idUnit2, uni1 );
					base->setUnit( idUnit1, uni2 );
				} else {
					uni2->addNumber( uni1->getNumber() );
					base->setUnit( idUnit1, 0 );
					delete uni1;
				}
			} else if (!uni1) {
				lord->setUnit( idUnit2, 0 );
				base->setUnit( idUnit1, uni2 );
			} else if (!uni2) {
				lord->setUnit( idUnit2, uni1 );
				base->setUnit( idUnit1, 0 );
			}
		} else {
			if( idUnit2 <= MAX_UNIT ) {
				uni2 = base->getUnit( idUnit2 );
			}
			if( uni1 && uni2 ) {
				if( uni1->getCreature() != uni2->getCreature() ) {
					base->setUnit( idUnit2, uni1 );
					base->setUnit( idUnit1, uni2 );
				} else {
					uni1->addNumber( uni2->getNumber() );
					lord->setUnit( idUnit2, 0 );
					delete uni2;
				}
			} else {
				base->setUnit( idUnit2, uni1 );
				base->setUnit( idUnit1, 0 );
			}
		}
	}
}




/*!

*/

void Analyst::socketModif()
{
	ialogV( "SO_MODIF" );
	switch( _socket->getCla2() ) {
	case C_MOD_MAP:
		socketModifMap();
		break;
	case C_MOD_CELL:
		socketModifCell();
		break;
	case C_MOD_PLAYER:
		socketModifPlayer();
		break;
	case C_MOD_LORD:
		socketModifLord();
		break;
	case C_MOD_BASE:
		socketModifBase();
		break;
	case C_MOD_BUILD:
		socketModifBuilding();
		break;
	case C_MOD_ARTEFACT:
		socketModifArtefact();
		break;
	case C_MOD_CREATURE:
		socketModifCreature();
		break;
	case C_MOD_EVENT:
		socketModifEvent();
		break;
	}
}

void Analyst::socketModifMap()
{
	int h = _socket->readInt();
	int w = _socket->readInt();
	_map->newUnknownMap( h, w );
}

void Analyst::socketModifCell()
{
	int row = _socket->readInt();
	int col = _socket->readInt();
	int type = _socket->readInt();
	/* XXX: unused... uchar diversification = */ _socket->readChar();
	int a4 = _socket->readInt();
	int a5 = _socket->readInt();
	int a6 = _socket->readInt();
	int a7 = _socket->readInt();
	_map->changeCell( row, col, type, a4, a5, a6, a7 );
}

void Analyst::socketModifLord()
{
	switch( _socket->getCla3() ) {
	case C_LORD_VISIT:
		socketModifLordVisit();
		break;
	case C_LORD_NEW:
		socketModifLordNew();
		break;
	case C_LORD_MOVE:
		{
			char lord = _socket->readChar();
			int nb = _socket->readInt();
			_lords.at( lord )->setBaseCharac( MOVE, nb );
			break;
		}
	case C_LORD_MAXMOVE:
		{
			char lord = _socket->readChar();
			int nb = _socket->readInt();
			_lords.at( lord )->setBaseCharac( MAXMOVE, nb );
			break;
		}
	case C_LORD_SP:
		{
			char lord = _socket->readChar();
			int nb = _socket->readInt();
			_lords.at( lord )->setBaseCharac( TECHNICPOINT, nb );
			break;
		}
	case C_LORD_MAXSP:
		{
			char lord = _socket->readChar();
			int nb = _socket->readInt();
			_lords.at( lord )->setBaseCharac( MAXTECHNICPOINT, nb );
			break;
		}
	case C_LORD_MORALE:
		{
			char lord = _socket->readChar();
			char nb = _socket->readChar();
			_lords.at( lord )->setBaseCharac( MORALE, nb );
			break;
		}
	case C_LORD_LUCK:
		{
			char lord = _socket->readChar();
			char nb = _socket->readChar();
			_lords.at( lord )->setBaseCharac( LUCK, nb );
			break;
		}
	case C_LORD_EXP:
		{
			char lord = _socket->readChar();
			int nb = _socket->readInt();
			_lords.at( lord )->setBaseCharac( EXPERIENCE, nb );
			break;
		}
	case C_LORD_ATT:
		{
			char lord = _socket->readChar();
			char nb = _socket->readChar();
			_lords.at( lord )->setBaseCharac( ATTACK, nb );
			break;
		}
	case C_LORD_DEF:
		{
			char lord = _socket->readChar();
			char nb = _socket->readChar();
			_lords.at( lord )->setBaseCharac( DEFENSE, nb );
			break;
		}
	case C_LORD_POW:
		{
			char lord = _socket->readChar();
			char nb = _socket->readChar();
			_lords.at( lord )->setBaseCharac( POWER, nb );
			break;
		}
	case C_LORD_KNOW:
		{
			char lord = _socket->readChar();
			char nb = _socket->readChar();
			_lords.at( lord )->setBaseCharac( KNOWLEDGE, nb );
		}
		break;
	case C_LORD_UNIT:
		socketModifLordUnit();
		break;
	case C_LORD_REMOVE:
		socketModifLordRemove();
		break;
	}
}

void Analyst::socketModifLordVisit()
{
	/*uchar num = */_socket->readChar();
	int row = _socket->readInt();
	int col = _socket->readInt();
	uchar id = _socket->readChar();
	uchar present = _socket->readChar();

		/// XXX: memory leak no new -> use _lords (cf game.cpp too !!)
		GenericLord * lord = _lords.at( id );
	
		if( present == 1 ) {

			//new GenericLord();
			//lord->setActive( true );
			//lord->setId( id );
			/// XXX: change
			lord->setOwner( 0 );
			lord->moveTo( _map->at( row, col ) );
			//_lords.at( id ) = lord;

		} else {
			lord->getCell()->setLord(NULL);
		}
}

void Analyst::socketModifLordNew()
{
	int row = _socket->readInt();
	int col = _socket->readInt();
	uchar id = _socket->readChar();

	GenericLord * lord = _lords.at( id );
	lord->moveTo( _map->at( row, col ) );
	lord->setOwner( _player );

	_player->addLord( lord );
}

void Analyst::socketModifLordUnit()
{
	uchar id = _socket->readChar();
	uchar num = _socket->readChar();
	uchar race = _socket->readChar();
	uchar level = _socket->readChar();
	int nb = _socket->readInt();
	uchar move = _socket->readChar();
	int health = _socket->readInt();

	GenericFightUnit * unit = 0;
	unit =_lords.at( id )->getUnit( num );
	if(!unit){
		unit = new GenericFightUnit();
		unit->setCreature( race, level );
		unit->setMove( move );
		unit->setHealth( health );
	}
	
	if( nb == 0  ){
		if(_lords.at( id )->countUnits()>1){
			unit->setNumber( nb );
			delete unit;
			unit = 0;
		}
	} else {
		unit->setNumber( nb );
	}
	
	_lords.at( id )->setUnit( num, unit );
}

void Analyst::socketModifLordRemove()
{
	int idLord = _socket->readChar();
	GenericLord * lord = _lords.at( idLord );
	GenericPlayer * player = lord->getOwner();

	lord->removeFromGame();

	/* if our turn (numTurn !=-1) pass, else do nothing*/
	if(InTurn()){
		if( player && player == _player ) {
			/*our lord is dead */
			_socket->sendLordTurn( PL_PASS_TURN );
		} else {
			/* enemy */
			_socket->sendLordTurn( PL_SAME_LORD );
		}
	}
}

void Analyst::socketModifPlayer()
{
	switch( _socket->getCla3() ) {
	case C_PLAY_RESS: {
		uchar ress = _socket->readChar();
		_player->setResource( ress, _socket->readInt() );
		}
		break;
	case C_PLAY_PRICE: {
		uchar ress = _socket->readChar();
		uint price = _socket->readInt();
		_player->getPriceMarket()->setResourcePrice( ress, price);
		}
		break;
	}
}

void Analyst::socketModifBase()
{
	switch( _socket->getCla3() ) {
	case C_BASE_NEW:
		socketModifBaseNew();
		break;
	case C_BASE_OWNER:
		socketModifBaseOwner();
		break;
	case C_BASE_NAME:
		socketModifBaseName();
		break;
	case C_BASE_BUILDING:
		socketModifBaseBuilding();
		break;
	case C_BASE_UNIT:
		socketModifBaseUnit();
		break;
	case C_BASE_POPUL:
		socketModifBasePopulation();
		break;
	}
}

void Analyst::socketModifBaseNew()
{
	uchar race = _socket->readChar();
	int row = _socket->readInt();
	int col = _socket->readInt();
	int population = _socket->readInt();
	uchar id = _socket->readChar();
	int nb = _socket->readChar();

	GenericBase * base = new GenericBase();
	base->setRace( race );
	base->setPosition( _map->at( row, col ) );
	base->setPopulation( population );
	_map->computeStoppable( base );
	base->setId( id );/// XXX: to inv ??
	base->isUnitBought( false );

	for( int i = 0; i < nb; i++ ) {
		base->addForbiddenBuilding( _socket->readChar() );
	}
}

void Analyst::socketModifBaseOwner()
{
	int row = _socket->readInt();
	int col = _socket->readInt();
	GenericBase * base = (GenericBase*)_map->at( row, col )->getBase();
	if( _socket->readChar() == _player->getNum() ) {
		_player->addBase( base );
		base->setOwner( _player );
	} else {
		/// XXX: improve management of base of other player
		if( base->getOwner() == _player ) {
			base->setOwner( 0 );
			_player->removeBase( (GenericBase*)base );
		}
	}
}

void Analyst::socketModifBaseName()
{
	int row = _socket->readInt();
	int col = _socket->readInt();
	uint length = _socket->readInt();
	QString name;
	
	for( uint i = 0; i < length; i++ ) {
		name.ref(i) = _socket->readChar();
	}
	
	GenericBase * base = _map->at( row, col )->getBase();
	base->setName( name );
}

void Analyst::socketModifBasePopulation()
{
	int row = _socket->readInt();
	int col = _socket->readInt();
	uint popul = _socket->readInt();
	GenericBase * base = (GenericBase*) _map->at( row, col )->getBase();

	if(base)
		base->setPopulation(popul);	
}

void Analyst::socketModifBaseBuilding()
{
	int row = _socket->readInt();
	int col = _socket->readInt();
	uchar level = _socket->readChar();
	bool create = (bool)_socket->readChar();
	//ialogN("base building row %d, col %d, level %d, create %d",row,col,level,create);

	if( _map->at( row, col )->getBase() ) {
		if( create ) {
			GenericInsideBuilding * building = new GenericInsideBuilding();
			building->setRace( _map->at( row, col )->getBase()->getRace() );
			building->setLevel( level );
			_map->at( row, col )->getBase()->addBuilding( building );
		} else {
			GenericInsideBuilding * building =  _map->at( row, col )->getBase()->getBuildingByType( level );
			_map->at( row, col )->getBase()->removeBuilding( building );
		}
	} else {
		logEE( "Base not found" );
	}
}

void Analyst::socketModifBaseUnit()
{
	int row = _socket->readInt();
	int col = _socket->readInt();
	if( _map->at( row, col )->getBase() ) {
		GenericBase * base = (GenericBase *)_map->at( row, col )->getBase();
		uchar race = _socket->readChar();
		uchar level = _socket->readChar();
		int number = _socket->readInt();
		Creature * creature = DataTheme.creatures.at( race, level );
		base->addGarrison( creature, number );
	}
}

void Analyst::socketModifBuilding()
{
	switch( _socket->getCla3() ) {
		case C_BUILD_NEW: {
			GenericBuilding * building = new GenericBuilding();
			int row = _socket->readInt();
			int col = _socket->readInt();
			building->setPosition( _map->at( row, col ) );
			uchar type = _socket->readChar();
			building->setType( type );
			_map->computeStoppable( building );
		}
		break;
		case C_BUILD_OWNER: {
			int row = _socket->readInt();
			int col = _socket->readInt();
			if( _socket->readChar() == _player->getNum() ) {
				_player->addBuilding( _map->at( row, col )->getBuilding() );
			}
		}
	}
}

void Analyst::socketModifArtefact()
{
	switch( _socket->getCla3() ) {
	case C_ART_DELLORD: {
		uint type = _socket->readInt();
		char lord = _socket->readChar();
		_lords.at( lord )->getArtefactManager()->removeArtefactByType( type );
	} break;
	case C_ART_ADDLORD: {
		uint type = _socket->readInt();
		char lord = _socket->readChar();
		if( ! _lords.at( lord )->getArtefactManager()->hasArtefactType( type ) ) {
			_lords.at( lord )->getArtefactManager()->addArtefact( type );
		}
	} break;
	}
}

GenericArtefact * Analyst::getArtefactById( int id )
{
	GenericArtefact * ret = 0;
	for( uint i = 0; i < _events.count(); i++ ) {
		if( ( _events.at( i )->getType() == GenericEvent::EventArtefact ) &&
		  _events.at( i )->getArtefact()->getId() == id ) {
			ret = _events.at( i )->getArtefact();
		}
	}
	return ret;
}

GenericPlayer * Analyst::getPlayer( uint num )
{
	GenericPlayer * ret = 0;
	
	if( num < _players.count() ) {
		ret = _players.at( num );
	}
	
	return ret;
}

void Analyst::socketModifEvent()
{
	switch( _socket->getCla3() ) {
	case C_EVENT_NEW: {
		int row = _socket->readInt();
		int col = _socket->readInt();
		GenericEvent::EventType type = (GenericEvent::EventType) _socket->readChar();

		GenericEvent * event = new GenericEvent();
		event->setCell( _map->at( row, col ) );
		_map->at( row, col )->setEvent( (GenericEvent*)event );

		if( type == GenericEvent::EventArtefact ) {
			int id = _socket->readInt();
			uchar typeArtefact = _socket->readChar();

			GenericArtefact * artefact = new GenericArtefact();
			event->setArtefact( artefact );
			artefact->setId( id );
			artefact->setType( typeArtefact );
		} else if( type == GenericEvent::EventBonus ) {
		uchar typeBonus = _socket->readChar();
		uchar nbParam = _socket->readChar();

		GenericBonus * bonus = new GenericBonus( );
		event->setBonus( bonus );
		bonus->setType( (GenericBonus::BonusType) typeBonus );
		for( uint i = 0; i < nbParam; i++ ) {
			bonus->addParam( _socket->readInt() );
		}
		bonus->setupBonus();
	} else if( type == GenericEvent::EventChest ) {
		uchar nbParam = _socket->readChar();

		GenericChest * chest = new GenericChest();
		event->setChest( chest );
		for( uint i = 0; i < nbParam; i++ ) {
			chest->addParam( _socket->readInt() );
		}
	}
	}
		break;
	case C_EVENT_DEL: {
		int row = _socket->readInt();
		int col = _socket->readInt();
		GenericEvent * event = _map->at( row, col )->getEvent();
		if( event ) {
			delete event;
			_map->at( row, col )->setEvent( 0 );
		}
		}
		break;
	}
}

void Analyst::socketModifCreature()
{
	switch( _socket->getCla3() ) {
	case C_CRE_NEW: {
		int row = _socket->readInt();
		int col = _socket->readInt();
		uchar race = _socket->readChar();
		uchar level = _socket->readChar();
		int nb = _socket->readInt();
		GenericMapCreature * creature = new GenericMapCreature();
		creature->setCreature( race, level );
		creature->setCategoryNumber( nb );
		creature->setCell( _map->at( row, col ) );
		_map->at( row, col )->setCreature( creature );
		}
		break;
	case C_CRE_UPDATE: {
		int row = _socket->readInt();
		int col = _socket->readInt();
		int nb = _socket->readInt();
		GenericMapCreature * creature = _map->at( row, col )->getCreature();
		if( creature ) {
			creature->setCategoryNumber( nb );
		}
		}
		break;
	case C_CRE_DEL: {
		int row = _socket->readInt();
		int col = _socket->readInt();
		GenericMapCreature * crea = _map->at( row, col )->getCreature();
		if( crea ) {
			delete crea;
			_map->at( row, col )->setCreature( 0 );
		}
		}
		break;
	}
}

void Analyst::socketTurn()
{
	ialogV( "SO_TURN" );

	switch( _socket->getCla2() ) {
	//case C_TURN_BEG:
	//	socketTurnBegin();
	//	break;
	case C_TURN_PLAY: {
			uint num = _socket->readChar();
			if( num == _player->getNum() ) {
				socketTurnBegin();
			} else {
				ialogV( "Player: %d,get num %d", num,_player->getNum() );
			}
		} break;
	case C_TURN_END:
		ialogC( "Should not happen (SO_TURN/C_TURN_END)" );
		break;
	case C_TURN_LORD:
		ialogC( "Should not happen (SO_TURN/C_TURN_LORD)" );
		break;
	case C_TURN_PLORD:{
		int nlord = _socket->readInt();
		ialogV("nlord %d",nlord);
		if(InTurn()){
			_numlords = _player->numLord();
			if( nlord >= _numlords || _numTurn==0 || _numlords == 0){
				ialogN("End turn");
				_socket->sendTurnEnd();
				_numTurn=-1;
			} else {
				ialogN("Play lord turn");
				playLordTurn(nlord);
			}
		}
		} break;
	}
}

void Analyst::socketTurnBegin()
{
	ialogN( "Start turn" );
	_calendar->newDay();
	if( _calendar->getDay() == 1 ) {
		_player->newWeek();
	}
	_player->newTurn();
	_numTurn=2 * _player->numLord() + 4; /* workaround for weirds game bugs */

	if( _player->numBase() > 0 ){
		for( int i = 0; i < (int)_player->numBase(); i++ ) {
			GenericBase * base = _player->getBase( i );
			manageBase( base );
		}
	}
	_socket->sendLordTurn( PL_SAME_LORD );
}

void Analyst::playLordTurn( int nlord )
{
	ialogV("init lord");
	analyzeLord( _player->getLord( nlord ) );
}

void Analyst::analyzeLord( GenericLord * lord )
{
	int movePoints = lord->getCharac( MOVE );
	GenericCell * cell = lord->getCell();
	GenericCell * destinationCell;
		
	struct aiData *data;
	data = new aiData;

	data->rowFlee = 1;
	data->colFlee = 1;
	data->startApprRow = 0;
	data->startApprCol = 0;
	data->destinationCell = cell;
	data->curPrior = (PR_LAST*10);
	data->mincost = movePoints + 10;
	data->fightCpt = _fightCpt;
	
	/* if a lord is already in a base */
	if( cell->getBase() != 0 ) {
		if( lord->getOwner() == _player ) {
				enterBase(lord, cell->getBase());
			}
	}

	/* if lord is powerful, prefer to fight and explore instead cumulate units */
	if(getPower(lord) > _SuffisantPower){
		lord->setPrio(PR_BASEOWNER,(PR_EXPLORE*10)+5);
		lord->setPrio(PR_CREAT,(PR_BASE*10)-1);
		//ialogV("prio %d",getPower(lord));
		//ialogV("creat %d",lord->getPrio(PR_CREAT));
	} else {
		lord->setPrio(PR_BASEOWNER,(PR_BASEOWNER*10));
		lord->setPrio(PR_CREAT,(PR_CREAT*10));
	}

	_map->getPath()->reinit( _map );
	_map->getPath()->computePath( cell );
	
	if(noPath(cell)){
		ialogV("no path");
		movePoints=0;
	}
	

	if( movePoints > 0 ) {
		analyzeLordMap( lord, data );
		_fightCpt = data->fightCpt;
		destinationCell = data->destinationCell;
		
		if( _fightCpt == PL_SAME_LORD_NOFIGHT) {
			destinationCell = randomPath(lord,data);
		}

		if(destinationCell  == cell){
			ialogN("no good");
			_fightCpt=PL_PASS_TURN;
		}

		ialogV("flee row %d, col %d, fightCpt %d",data->rowFlee,data->colFlee,_fightCpt);
		ialogV("startRow %d, startCol %d",cell->getRow(),cell->getCol());
		ialogV("destRow %d, destCol %d",destinationCell->getRow(),destinationCell->getCol());

		if(_fightCpt==PL_SAME_LORD_NOFIGHT){
		/* if no possibility to move, destination cell = starting cell and sanity check*/
			if(lord->computeCostMvt(destinationCell) > movePoints || (_map->getPath()->isPath(destinationCell)==false))  {
				destinationCell  = cell;
				_fightCpt=PL_PASS_TURN;
			}
			if(_map->getPath()->isPath(destinationCell)==true && destinationCell  != cell){
				QPtrStack<GenericCell> * path = _map->getPath()->giveCells( destinationCell );
				while( ! path->isEmpty() ) {
					GenericCell * movCell = path->pop();
					_socket->sendMvt(lord->getId(), movCell->getRow(), movCell->getCol() );
				}
				delete path;
			}
		}else{
			if(_map->getPath()->isNearPath(destinationCell)==true && destinationCell  != cell){
				QPtrStack<GenericCell> * path = _map->getPath()->giveNearCells( destinationCell );
				while( ! path->isEmpty() ) {
					GenericCell * movCell = path->pop();
					_socket->sendMvt(lord->getId(), movCell->getRow(), movCell->getCol() );
				}
				_socket->sendMvt(lord->getId(), destinationCell->getRow(), destinationCell->getCol() );
				delete path;
			}
		}
	} else {
		ialogV("Turn pass");
	 	_fightCpt = PL_PASS_TURN;
	}
	if(_fightCpt==PL_SAME_LORD_NOFIGHT){
		ialogV("Turn cont no meet");
		_fightCpt=PL_SAME_LORD;
	}
		_numTurn--;
		ialogV("Turn finish %d",_fightCpt);
	_socket->sendLordTurn(_fightCpt);
	delete data;
}

void Analyst::analyzeLordMap( GenericLord * lord ,struct aiData * data)
{
	unsigned int i, j;
	GenericCell * cell;

	int opPower = 0;
	//int cost;

	data->fightCpt = PL_SAME_LORD_NOFIGHT;
	/* the priority with lower value is the more important*/
		
	for( i = 0; i < _map->getHeight(); i++ ) {
		for( j = 0; j < _map->getWidth(); j++ ) {
			cell = _map->at( i, j );
			if(cell->getCoeff() != -1){
				if( cell->getLord() && ( lord->getPrio(PR_ENEMY) <= data->curPrior )  ) {
					if( cell->getLord()->getOwner() != _player ) {
						opPower = getRandPower( cell->getLord() );
						ialogN( "Try Enemy");
						ialogV( "row %d, col %d",cell->getRow(),cell->getCol());
						data->fightCpt=manageMeetings(lord,cell ,lord->getPrio(PR_ENEMY),opPower,true,data);
					}
				} 
				if( cell->getBase() && (lord->getPrio(PR_BASE)	<= data->curPrior )  )	{
					if( cell->getBase()->getOwner() != _player) {
						ialogN( "Try Base");
						ialogV( "row %d, col %d",cell->getRow(),cell->getCol());
						data->fightCpt=manageMeetings(lord,cell ,lord->getPrio(PR_BASE),0,false,data);
					}
				} 
				if( cell->getBase() && (lord->getPrio(PR_BASEOWNER)	<= data->curPrior )  )	{
					if( cell->getBase()->getOwner() == _player && cell->getBase()->isUnitBought() ) {
						ialogN( "Try Base Owner");
						ialogV( "row %d, col %d",cell->getRow(),cell->getCol());
						data->fightCpt=manageMeetings(lord,cell ,lord->getPrio(PR_BASEOWNER),0,false,data);
					}
				} 
				if( cell->getBuilding() && (lord->getPrio(PR_BUILD) <= data->curPrior) ) {
					if( cell->getBuilding()->getOwner() != _player ) {
						ialogN( "Try Build");
						ialogV( "row %d, col %d",cell->getRow(),cell->getCol());
						data->fightCpt=manageMeetings(lord,cell ,lord->getPrio(PR_BUILD),0,false,data);
					}
				} 
				if( cell->getEvent()){
					if(cell->getEvent()->getType()==GenericEvent::EventArtefact && ( lord->getPrio(PR_ART) < data->curPrior )  )	{
						ialogN( "Try Event-Artefact");
						ialogV( "row %d, col %d",cell->getRow(),cell->getCol());
						data->fightCpt=manageMeetings(lord,cell ,lord->getPrio(PR_ART),0,false,data);
					} 
					if( cell->getEvent()->getType()==GenericEvent::EventBonus && ( lord->getPrio(PR_BONUS) < data->curPrior ) )	{
						ialogN( "Try Event-Bonus");
						ialogV( "row %d, col %d",cell->getRow(),cell->getCol());
						data->fightCpt=manageMeetings(lord,cell ,lord->getPrio(PR_BONUS),0,false,data);
					} 
					if( cell->getEvent()->getType()==GenericEvent::EventChest && ( lord->getPrio(PR_CHEST)< data->curPrior ) )	{
						ialogN( "Try Event-chest");
						ialogV( "row %d, col %d",cell->getRow(),cell->getCol());
						data->fightCpt=manageMeetings(lord,cell ,lord->getPrio(PR_CHEST),0,false,data);
					}
				}
				if( cell->getCreature() ){
					if	(lord->getPrio(PR_CREAT) < data->curPrior) {
						ialogN( "Try Creat");
						ialogV( "row %d, col %d",cell->getRow(),cell->getCol());
						opPower = getCreaturePower( cell->getCreature() );
						data->fightCpt=manageMeetings(lord,cell ,lord->getPrio(PR_CREAT),opPower,false,data);
					} 
				}
			} //coeff
			if ( lord->getPrio(PR_EXPLORE) <= data->curPrior) {
				/* ai explore map */
				if(cell->getType() == 0){
					GenericCell * dest;
					dest = findNearCellReach(cell);
					if(dest){
						data->fightCpt=manageMeetings(lord,dest ,lord->getPrio(PR_EXPLORE),0,false,data);
					}
				}
			}
		}//for i
	}//for j

	/* tune ai based on enemy stength */
	if(opPower > _SuffisantPower  ) {
		_SuffisantPower = opPower ;
		ialogV("opPower %d",opPower);
	} else {
		_SuffisantPower = 1000;
	}
}


void Analyst::socketGame()
{
	ialogN( "SO_GAME" );
	switch( _socket->getCla2() ) {
		case C_GAME_END :	{
		  reinitAi();
		  ialogN( "SO_GAME_END" );
			}
			break;
		case C_GAME_LOST : {
		  int nb = _socket->readChar();
			if(nb != _player->getNum()) {
				ialogN("player %d has lost",nb);
			} else {
				ialogN("I'm the loser, i'm num %d",nb);
			}
		  ialogN( "SO_GAME_LOST" );
			}
			break;
		case C_GAME_WIN : {
		  int nb = _socket->readChar();
			if(nb == _player->getNum()) {
				ialogN("I'm the winner, i'm num %d",nb);
			} else {
				ialogN("player %d has win",nb);
			}
		  ialogN( "SO_GAME_WIN" );
			}
			break;
	 	case C_GAME_BEGIN : {
			ialogN( "SO_GAME_ BEGIN" );
			}
			break;
		case C_GAME_INFO :
			socketGameInfo();
			break;
		default : {
			ialogN( "Should not happens" );
			ialogN("sock %d ",_socket->getCla2());
			}
			break;
	}
		
}

void Analyst::socketGameInfo()
{
	ialogN( "SO_GAME_INFO " );
	switch( _socket->getCla3() ) {
	case C_INFOPLAYER_TEAM: {
			uchar player = _socket->readChar();	
			uchar teamId = _socket->readChar();
			if( getPlayer( player ) ) {
				getPlayer( player )->setTeam( teamId );
			}
		} break;
	case C_INFOPLAYER_NAME:
		break;
	}

}


int Analyst::getRandPower( GenericLord * lord )
{
	int power = 0;

	for( int i = 0; i < MAX_UNIT; i++ ) {
		GenericFightUnit * lordUnit = lord->getUnit( i );
		if( lordUnit ) {
			uint number = DataTheme.getRandomInCategory( (uchar)DataTheme.computeCategory( lordUnit->getNumber() ) );
			power += lordUnit->getCreature()->getAttack() * number;
		}
	}
	return power;
}

int Analyst::getCreaturePower( GenericMapCreature * creature )
{
	int power = 0;

	ialogV("att %d, num %d",creature->getCreature()->getAttack() , creature->getCreatureNumber());
	if( creature->isEstimated() ) {
		uint estimatedNb = DataTheme.getRandomInCategory( creature->getCategoryNumber() );
		power = creature->getCreature()->getAttack() * estimatedNb;
	} else {
		power = creature->getCreature()->getAttack() * creature->getCreatureNumber();
	}

	return power;
}


int Analyst::getPower( GenericLord * lord )
{
	int power = 0;

	for( int i = 0; i < MAX_UNIT; i++ ) {
		GenericFightUnit * lordUnit = lord->getUnit( i );
		if( lordUnit ) {
			power += lordUnit->getCreature()->getAttack() * lordUnit->getNumber();
		}
	}
	return power;
}


void Analyst::manageBase(GenericBase * base)
{
	// scan all buildings looking for creatures
	uint count = base->getBuildingCount();
	uint i;
	ialogN("Try buy");
	for( i = 0; i < count; i++ ) {
		GenericInsideBuilding * building=base->getBuilding( i );
		InsideAction * action = DataTheme.bases.at( base->getRace() )->getBuildingModel( building->getLevel() )->getAction();
		if(action){		
			int race = action->getParam( 0 );
			int level = action->getParam( 1 );
			Creature * creature = DataTheme.creatures.at( race, level);
//  		int max =_player->computeBuyCreatureMax( creature );
			for(int  j = base->getCreatureProduction(creature);j>0; j-- ) {
				if(_player->canBuy( creature, j ) ){
					ialogN("BUY  num %d",j);
					_player->buy(creature,j);
					_socket->sendBaseUnit( base, creature, j );
					base->buyCreature( creature, j );
					base->isUnitBought(true); 
					break;
				}
			}
		}
	}

	int bought = 0;
	GenericBaseModel * baseModel = DataTheme.bases.at( base->getRace() );
	int modelCount = baseModel->getBuildingCount();
	InsideBuildingModel * model;

	for( i = 0; i < (uint) modelCount; i++ ) {
		GenericInsideBuilding * building= base->getBuildingByType( i );
		if( _player && _socket &&  !building && bought == 0) {
			model = DataTheme.bases.at( base->getRace() )->getBuildingModel( i ) ;
			/* ai don't buy buildings that not implement actions */
			if (model->getAction()) {
				if(model->getAction()->getType() != INSIDE_NONE) {
					if( _player->canBuy( model ) ) {
						_socket->requestBuilding( base, i, true );
						ialogN("Request Building model %d",i);
						bought = 1;
					} else {
						//logDD("Can't buy, Not enough ressources to buy this building" );
					}
				}
			}
		}
	}
}


void Analyst::enterBase(GenericLord * lord,GenericBase * base )
{
	int i;
	int j;
	ialogN("Enter base");

	base->enter( lord );
		for(i=0;i<MAX_UNIT;i++){
			for(j=0;j<MAX_UNIT;j++){
				if(lord->getUnit(i) && lord->getUnit(j) && i!=j){
					if(lord->getUnit(i)->getCreature()==lord->getUnit(j)->getCreature()){
						_socket->sendExchangeUnit( lord, i, lord, j );
					}
				}
			}
		}
	if(lord->countUnits()<MAX_UNIT){
		for(i=0;i<MAX_UNIT;i++){
			for(j=0;j<MAX_UNIT;j++){
				if(lord->getUnit(j) && base->getGarrisonUnit(i)){
					if(lord->getUnit(j)->getCreature()==base->getGarrisonUnit(i)->getCreature()){
						_socket->sendExchangeBaseUnit( base, i, lord, j );
					}
				}
			}
		}
		for(i=0;i<MAX_UNIT;i++){
			for(j=0;j<MAX_UNIT;j++){
				if(!lord->getUnit(j) && base->getGarrisonUnit(i)){
					_socket->sendExchangeBaseUnit( base, i, lord, j );
					//ialogN("Exch ,pos i %d ,pos j %d",i,j);
				} 
			}
		}
	}
	base->isUnitBought(false);
	base->out( lord );
}


int Analyst::manageMeetings(GenericLord * lord,GenericCell * cell ,int prior,int opPower, bool isFlee, struct aiData * data )
{
	int cost;
	int startRow = lord->getCell()->getRow();
	int startCol = lord->getCell()->getCol();
	int myPower = getPower( lord );
	GenericCell * tcell;
	int dRow = cell->getRow();
	int dCol = cell->getCol();
	int movePoints = lord->getCharac( MOVE );

	if(opPower>0)
		ialogN("opPower %d, myPower %d",opPower,myPower);	

	tcell=_map->getPath()->getNearCell(	cell );
	/* workaround for getNearCell bug? */
	if(tcell->getRow() > 0 && tcell->getCol() > 0) {
		cost = _map->getPath()->getDist( tcell );
	} else {
		cost = 10000;
	}
	/* if they are more targets with same priority, use that with minimal cost*/
	if( data->curPrior > prior ) {
		data->mincost = movePoints+10;
	}
	
	/* is the target is reachable, go */
	if( ( cost < data->mincost ) && ( cost <= movePoints )
			&& ( cost > 0 ) && ( myPower > opPower ) ) {
		data->destinationCell = cell;
		data->mincost = cost;
		data->curPrior = prior;
		//ialogN("Meeting");
		return PL_SAME_LORD;
		
	/* else go near target (or flee if enemy is more powerful) */
	} else if( cost < ( movePoints + 5 ) && cost != -1 ) {
		data->startApprRow = std::min(startRow,dRow);
		data->startApprCol = std::min(startCol,dCol);
	
		if( myPower < opPower && isFlee) {
			data->startApprRow = std::max(startRow,dRow);
			data->startApprCol = std::max(startCol,dCol);
			data->rowFlee = flee(startRow,dRow);
			data->colFlee = flee(startCol,dCol);
			data->curPrior = prior;
		}
	}
	return PL_SAME_LORD_NOFIGHT;
}

GenericCell * Analyst::randomPath(GenericLord * lord, struct aiData * data)
{

		int movePoints = lord->getCharac( MOVE );
		int startRow= lord->getCell()->getRow();
		int startCol= lord->getCell()->getCol();
		int sameTry=0;
		int moreTry=0;
		int i,j;
		float he = (float)_map->getHeight();
		float wi = (float)_map->getWidth();
		 
		do {
			do {
				if( data->startApprRow ) {
					i = data->startApprRow + ( data->rowFlee)*(int)( (rand()/(RAND_MAX+1.0)) * (float) movePoints);
					moreTry++;
					if(moreTry>19 ) {
						i=data->startApprRow;
					}	
				} else {
					i=(int) (he*(rand()/(RAND_MAX+1.0)));
				}
			}while(i>=(int) he || i<0);
			moreTry=0;
			do{
				if(data->startApprCol){
					j=data->startApprCol+(data->colFlee)*(int)( (rand()/(RAND_MAX+1.0))* (float) movePoints);
					moreTry++;
					if(moreTry>19 ){
						j=data->startApprCol;
					}	
				} else {
					j=(int) (wi*(rand()/(RAND_MAX+1.0)));
				}
			}while(j>=(int) wi || j<0 );
			
			if( i == startRow && j == startCol ){
				sameTry++;
			} else {
				if( lord->computeCostMvt(_map->at(i, j))<= movePoints && (_map->getPath()->isPath(_map->at(i, j))==true) && lord->computeCostMvt(_map->at(i, j)) > 0)  {
					sameTry=30;
				}
			}
		} while(( lord->computeCostMvt(_map->at(i, j)) > movePoints || _map->getPath()->isPath(_map->at(i, j))==false) && sameTry<30 );
		return _map->at( i, j );
}

int Analyst::noPath(GenericCell * cell)
{

		uint row= cell->getRow();
		uint col= cell->getCol();
		uint i,j;
		uint initRow;
		uint initCol;
		uint finRow;
		uint finCol;

		initRow= row > 0 ? row-1 : 0;
		initCol= col > 0 ? col-1 : 0;
		finRow= row < _map->getHeight()-1 ? row+1 : _map->getHeight()-1;
		finCol= col < _map->getWidth()-1 ? col+1 : _map->getWidth()-1;

		for(i=initRow; i<=finRow; i++){
			for(j=initCol; j<=finCol; j++){
				if(_map->getPath()->isPath(_map->at(i,j)))
						return false;
			}
		}
		return true;
}

inline int Analyst::flee(int src, int dst)
{
	int tmp;
	
	tmp = ((src)>(dst) ? 1:  -1);

	return tmp;
}

GenericCell * Analyst::findNearCellReach(GenericCell * cell)
{

		uint row= cell->getRow();
		uint col= cell->getCol();
		uint i,j;
		uint initRow;
		uint initCol;
		uint finRow;
		uint finCol;
		uint height =_map->getHeight();
		uint width = _map->getWidth();
		GenericCell * tcell;

		initRow= row > 0 ? row-1 : 0;
		initCol= col > 0 ? col-1 : 0;
		finRow= row < height-1 ? row+1 : height-1;
		finCol= col < width-1 ? col+1 : width-1;

		for(i=initRow; i<=finRow; i++){
			for(j=initCol; j<=finCol; j++){
				tcell = _map->at(i,j);
				if(tcell){
					if(tcell->getType() != 0){	
						if(_map->getPath()->isPath(tcell))
							return tcell;
					}
				}
			}
		}
		return NULL;
}

