/*=============================================================================
	  ___________
	 /           \		Par Martin Robinson
	|   @#!$*%    |		logiciel libre GPL
	 \_______   _/
	 		 \ |  ___________
	    	  \| |   O   O   |
				 |  _______  |
				 | | | | | | |
===============================================================================
Preprocesseur
=============================================================================*/

#pragma once
#ifndef spiele_deja
#define spiele_deja
#include "main.hpp"

#include "level.hpp"
#include "ro.hpp"
#include "bot.hpp"
#include "shape.hpp"
#include "constchar.hpp"

/*constante
________________________*/

//liste de vecteur
//up down left right
const int LR[4] = {0,0,-1,1},
          UD[4] = {-1,1,0,0};

/*classe
__________________________*/

template <int _W, int _H>
class spiele{
	template <int _LVLW,int _LVLH> friend class spieleUI;
protected:
    level<_W,_H> _lvl;		//le level
    bot _robot[20];			//tous les bots
	int _robotQty;			//quantité totale de bot
    trans _transbot;		//liste de bots unifiés
    naut _nautbot;			//liste de bots éparpillés
    shape _shapebot[9];      //tableau des formes posibles
    int _olShape,			//forme actuelle
        _nuShape;			//forme nouvelle
    bool _alternance;		//_alternance pour le block hazard
public:
    bool gameover,			//indicateur de fin de partie,échoué  , publique
         gamewin;			//indicateur de fin de partie,gagné   , publique
    void init(const char *, const char * thoseShape[9]);//initialisation
    void control(int k);		                        //controle du robot
    bool process();			                            //tout le moteur du jeux tourne ici
private:
    bool findTwoNeighborInNautbot();					//quand tous les bots sont éparpillé, trouve en deux à proximité
    void scramble();									//éparpille tous les bots
    bool shapeFitInLevel(shape & form);					//test si la forme peut se placer dans le level
    bool noOneInTransbotCollideWithLevelRel(int, int);	//test si au moin un seul bot du _transbot entre en collision avec le level suivant la permutation suggérer
    bool _transbotChangedShape();						//est-ce que le _transbot à changé de forme ?
    bool contactFusionWithTransbot();					//est-ce que le _transbot vien d'acceuillir un nouveau bot ?
    char fromLevelAt(int x, int y);						//retourne le caractere du niveau au coordonné
    char fromLevelAt(int i);							//retourne le caractere du niveau a l'index
};

/*methode
___________________________*/

//initialisation
template <int _W, int _H>
void spiele<_W,_H>::init(const char * level, const char * thoseShape[9])
{
	_alternance = false;
	_robotQty=0;
    _lvl.init(level);    //initialise level
    //find bot starter in level
    for (int i=0;i<20;i++)
    {
        char botStart = i + 49;     //from 1 to 9
        if (i>8)
            botStart += 7;          //and A to K
        int botIndex = findIndex<_W,_H>(level, botStart);
        if (botIndex > 0)
        {
            _robot[_robotQty].init(xFromIndex<_W,_H>(botIndex), 
				                 yFromIndex<_W,_H>(botIndex),
								 _W, _H);
            _nautbot.push(&_robot[_robotQty]);
			_robotQty++;
        }
    }
    //set shapes
    for (int i=0;i<9;i++)
        _shapebot[i].build(thoseShape[i]);
    _nuShape=-1;
    //set game state
    gameover = gamewin = false;
}

//controle
template <int _W, int _H>
void spiele<_W,_H>::control(int k)
{
	if (k<0)            //-1 donne scramble
		scramble();
	else if (k<9)       //formes respectives de 1 a 9
		_nuShape = k;
}

//ici que le moteur du jeux tourne
template <int _W, int _H>
bool spiele<_W,_H>::process()
{
	//renumérauté les robots
	_transbot.unite();
	_nautbot.unite();
	//_transbot movement (ceux qui sont attaché)
	//je ne peut le mettre dans le level hasard parce que ce dernier verifie tout les bots
	//si un seul entre en collision
	if (noOneInTransbotCollideWithLevelRel(0,1))
		_transbot.relMove(0,1);
    //le changement de forme
	if (_transbotChangedShape())
	{
		_transbot.reset();
		while(!_transbot.end())
			_transbot.next()->type='0';
	}
	//level hazard
	_transbot.reset();
	while(!_transbot.end())
	{
			switch(fromLevelAt(_transbot.peek()->indexWithRel(0,1)))
			{
			case '<':
				if (noOneInTransbotCollideWithLevelRel(-1,0))
					_transbot.relMove(-1,0);
				break;
			case '>':
				if (noOneInTransbotCollideWithLevelRel(1,0))
					_transbot.relMove(1,0);
				break;
			case '!':
				scramble();
				_nuShape = -1;
				break;
			case '@':
				//gamewin
				gamewin = true;
				break;
			}
		_transbot.next();
	}
	//nautbots mouvement (ceux qui sont perdu dans le level)
	_nautbot.reset();
	while(!_nautbot.end())
	{
		//level hazard
		switch(_lvl.at(_nautbot.peek()->indexWithRel(0,1)))
		{
		case '<':
			if (fromLevelAt(_nautbot.peek()->indexWithRel(-1,0))==' '  &&
				!_nautbot.peekCollideWithRel(-1,0))
					_nautbot.peek()->relMove(-1,0);
			break;
		case '>':
			if (fromLevelAt(_nautbot.peek()->indexWithRel(1,0))==' ' &&
				!_nautbot.peekCollideWithRel(1,0))
					_nautbot.peek()->relMove(1,0);
			break;
		case '!':
			//assurer qu'aucun bot ne reste sur ce block
			if (_alternance)
			{
				if (fromLevelAt(_nautbot.peek()->indexWithRel(1,0))==' ' &&
					!_nautbot.peekCollideWithRel(1,0))
						_nautbot.peek()->relMove(1,0);
				_alternance = false;
			}
			else
			{
				if (fromLevelAt(_nautbot.peek()->indexWithRel(-1,0))==' '  &&
					!_nautbot.peekCollideWithRel(-1,0))
						_nautbot.peek()->relMove(-1,0);
				_alternance = true;
			}
			break;
		case ' ':
			//gravity
			if (fromLevelAt(_nautbot.peek()->indexWithRel(0,1)) ==' ' &&
				!_nautbot.peekCollideWithRel(0,1))
						_nautbot.peek()->relMove(0,1);
			break;
		}
		_nautbot.next();
	}
	//quand le gros touche un petit ,il se joingnent
	if (contactFusionWithTransbot())
		_olShape=-1;
		
	return gamewin;
}

/*PRIVATE
_________________________________________*/

//est-ce que la nouvelle forme peut être placé dans le level à la position actuele
template <int _W, int _H>
bool spiele<_W,_H>::shapeFitInLevel(shape & form)
{
    for (int i=0;i<form.size();i++)
	    if (fromLevelAt(indexFromXY<_W,_H>(form.col(i),form.row(i))) != ' ')
		    return false;
    return true;
}

//est-ce qu'il y a un seul bot qui entre en collision avec le level, suivant la permutation suggérer
template <int _W, int _H>
bool spiele<_W,_H>::noOneInTransbotCollideWithLevelRel(int vx, int vy)
{
	_transbot.reset();
	while(!_transbot.end())
		if (fromLevelAt(_transbot.next()->indexWithRel(vx,vy)) !=' ')
			return false;
	return true;
}

//est-ce que le robot arrive à changer de forme? le changement de forme se produit.
template <int _W, int _H>
bool spiele<_W,_H>::_transbotChangedShape()
{
    if (_olShape != _nuShape)
    {
        //est-ce qu'aux moin on peut trouver deux bots côte à côte?
        if (!findTwoNeighborInNautbot())
        {
            _nuShape = _olShape;
            gameover = true;
            return false;
        }
	    //adjust to map
	    if (!_shapebot[_nuShape].resize(_transbot.size()))
	        return false;
		//get last in swarm
        int i = _transbot.size()-1;
	    int sx = _transbot.get(i)->col(),
		    sy = _transbot.get(i)->row();
		//first point is starter
	    _shapebot[_nuShape].adjust(sx,sy);
	    //test every position if ok, set all
	    if (shapeFitInLevel(_shapebot[_nuShape]))
	    {
	        _transbot.reshape(_shapebot[_nuShape]);
	        _olShape=_nuShape;
	        return true;
	    }
    }
    return false;
}

//si tout les bots sont éparpillés, est-ce qu'il y a au moin deux bots adjacent
//pour tout les bots, ajouter le au swarm, verifier s'il y contact, sinon retirer le, au suivant
//retourn faux si aucun n'est trouvé
template <int _W, int _H>
bool spiele<_W,_H>::findTwoNeighborInNautbot()
{
    if (_transbot.size()==0)
    {
        for (int i=0;i<_nautbot.size();i++)
        {
            _transbot.push(_nautbot.remove(0));
            if (contactFusionWithTransbot())
                return true;
            _nautbot.push(_transbot.pop());
        }
        return false;
    }
    return true;
}

//éparpille les bots
//vide une stack dans une autre
template <int _W, int _H>
void spiele<_W,_H>::scramble()
{
	//vide le _transbot dans le _nautbot
    while(_transbot.size())
        _nautbot.push(_transbot.pop());
	_nautbot.unite();
    _olShape = -1;	//élimine la  forme
    _nuShape = -1;
}

//est-ce que le _transbot accueil un nouveau bot?
//verification si il y a contact entre le gros swarm et un robot perdu
template <int _W, int _H>
bool spiele<_W,_H>::contactFusionWithTransbot()
{
	//pour chaque bot du _nautbot
    _nautbot.reset();
    while(!_nautbot.end())
    {
		//qui se compare à chaque bot du _transbot
        _transbot.reset();
        while (!_transbot.end())
        {
			//pour les 4 côtés du membre _nautbot, est-ce qu'ily a un membre du _transbot
            for (int j=0;j<4;j++)
                if (_nautbot.peek()->indexWithRel(LR[j],UD[j]) == _transbot.peek()->index())
                {
					//si oui , transfere le du _nautbot vers le _transbot et retourne un succes
                    _transbot.push(_nautbot.peekRemove());
                    return true;
                }
            _transbot.next();
        }
        _nautbot.next();
    }
    return false;
}

//retourne le caractere a l'index du level
template <int _W, int _H>
char spiele<_W,_H>::fromLevelAt(int i)
{
    char tmp = _lvl.at(i);
	//exclusion des caracteres de point de départ des bots
	//il ne servent qu'a l'initialisation
    if ((tmp <= '9' && tmp >= '1') ||
        (tmp <= 'K' && tmp >= 'A'))
        return ' ';
    return tmp;
}

//retourne le caractere au coordonné du level
template <int _W, int _H>
char spiele<_W,_H>::fromLevelAt(int x, int y)
{
    return fromLevelAt(indexFromXY<_W,_H>(x,y));
}


#endif
