/*_________________________________________________

___________________________________________________
preprocessor*/
#pragma once
#ifndef _TILE
#define _TILE

#include "base.hpp"
#include "map.hpp"

/*_________________________________________________
object*/

//base virtual object
//contain back object method
//contain iterator
class tileBase : public base{
protected:
	int _col;                           //relative postion on grid
	int _row;
    int _rotation;                      //relative rotation
    bool under(int col, int row);       //test if position is covered by it
    char backName(int col, int row);    //give the name of the back object
    char backAt(int col, int row);      //give the charactere at pos on the back object
public:
    tileBase(base *);                   //constructor (assemble with map or another tile)
    void rotate();                      //user ask rotation 90 cw
    void move(int col, int row);        //user specified move at on the grid
    void reset();                       //reset iterator
    virtual bool end();                 //test if iterator on the end
    virtual void next();                //increase iterator
};

//______________________________________
//-------------------3 tile object proto
//object templatized for use with three square tile
//it is a specialisation of the above object
template <char NAME ,char A, char B, char C>
class tile : public tileBase{
    char _at(int scol, int srow);       //return tile property ( _ P O ) 
                                        //at position not relative to the grid
public:
    tile(base*back):tileBase(back){};   //constructor (assemble with map or another tile)
	char getAt (int col, int row);      //return tile property or map property
                                        //or another tile property at given position
    bool valid();                       //return validity of the grid contained
    char getName(int,int);              //return the name of the tile
};

//-------------------3 tile object method
//get porperty (grid, current tile or previously place tile)
//at position relative to grid
template <char NAME ,char A, char B, char C>
char tile<NAME ,A,B,C>::getAt (int col, int row)
{
    int c;                      //temporary variable
    //validate coordinate so it does not clip out the grid
    if (col < 0 || col >=4 || row < 0 || row >=4)
        return ' ';
    //need to verify that the coordinate is under the tile
    if (!under(col,row))
        return backAt(col,row); //not under then show what behind
    //get character
    c = _at(col-_col, row-_row);
    if (c==' ')
        return backAt(col,row); //L shaped tile have one empty quadran
    return c;
}

//test the validity of the tile on the map
//then the entire map recursively
template <char NAME ,char A, char B, char C>
bool tile<NAME ,A,B,C>::valid ()
{
    char c,     //temporary var for current tile
         b;     //and behind on the grid or previously place tile
    //make sure the tile is not cliping out the grid
    if (_col <0 || _row <0 || _col >= 3 || _row >= 3)
        return false;
    //all four quadran will be checked
    for (int col=_col;col<_col+2;col++)
        for (int row=_row;row<_row+2;row++)
        {
            c = _at(col-_col,row-_row);     //current property
            b = backAt(col,row);            //behind
            if (c == 'O' && b != 'B')       //bear not on ice?
                return false;
            else if (c == 'P' && b != 'E')  //fish not in water?
                return false;
            else if (c == '_' && backName(col,row) != '~')  
                return false;               //"window" not directly on map
        }
    //then check if the grid was okay
//    if (_back)
//        return _back->valid();
    return true;
}

//get the current name at position
template <char NAME ,char A, char B, char C>
char tile<NAME ,A,B,C>::getName(int col, int row)
{
    char c;                             //temporary var
    if (under(col,row))
    {
        c = _at(col-_col, row-_row);
        if (c == 'P' || c == '_' || c == 'O')
            return NAME;
    }
    return backName(col,row);
}


//-------------------3 tile object protected method
//return tile property at position not related to grid but itself
//using template magic, there is no need for array storing the tile
//and there is no need for rotation
template <char NAME ,char A, char B, char C>
char tile<NAME ,A,B,C>::_at (int scol, int srow)
{
    //i build a magic number
    //first digit serve as position => 0 1
    //                                 2 3
    //second digit serve as rotation => 00 01   => 12 10
    //                                  02 03      13 11
    //then a blazing fast switch will return the needed character
    switch (scol+srow*2 + _rotation*10)
    {
    case 0:
    case 11:
    case 23:
    case 32:
        return A;
    case 1:
    case 13:
    case 22:
    case 30:
        return B;
    case 2:
    case 10:
    case 21:
    case 33:
        return C;
    }
    return ' ';
}

//______________________________________
//-------------------2 tile object proto
//specialisation of tile with 2 tile
template <char NAME ,char A, char B>
class tile<NAME ,A, B, ' '> : public tileBase 
{
    char _at(int,int);                  //return tile at pos not relative to grid
public:
    tile(base*back):tileBase(back){};   //constructor (assemble with map or another tile
	char getAt (int col, int row);      //return tile at pos relative to grid
    bool valid();                       //verify validity of the entire grid
    char getName(int,int);              //return name under position
    void next();                        //increase iterator
    bool end();                         //test if iterator is ended
};

//-------------------2 tile object method

//return tile at pos relative to grid
template <char NAME ,char A, char B>
char tile<NAME ,A,B,' '>::getAt (int col, int row)
{
    char c;
    //validate coordinate
    if (col < 0 || col >=4 || row < 0 || row >=4)
        return 0;
    //need to verify that the coordinate is under the tile
    if (!under(col,row))
        return backAt(col,row);
    //create index form coordinate and rotation
    c = _at(col-_col, row-_row);
    if (c == ' ')
        return backAt(col,row);
    return c;
}

//verify validity of the entire grid
template <char NAME ,char A, char B>
bool tile<NAME ,A,B,' '>::valid ()
{
    int w=1,        //temporary var
        h=1;        //telling the size from rotation
    char c,         //current property
         b;         //property from behind
    //verify the square if it get out of the map
    if (_col <0 || _row <0 || _col > 3 || _row > 3)
        return false;
    //with rotation i know if i can go on position closer to the edge
    switch(_rotation)
    {
    case 0:
    case 2:
        if (_col >= 3 || _row > 3)
            return false;
        w=2;
        break;
    case 1:
    case 3:
        if (_col >= 3 || _row > 3)
            return false;
        h=2;
    }
    //the real test
    for (int col=0;col<w;col++)
        for (int row=0;row<h;row++)
        {
            c = _at(col,row);
            b = backAt(_col+col,_row+row);
            if (c == 'O' && b != 'B')       //bear not on ice?
                return false;
            else if (c == 'P' && b != 'E')  //fish not in water?
                return false;
            else if (c == '_' && getName(_col+col,_row+row) != '~')
                return false;               //'window' not directly on grid?
        }
    return true;
}

//return name under position
template <char NAME ,char A, char B>
char tile<NAME ,A,B,' '>::getName(int col, int row)
{
    char c;
    //add piece detection
    if (under(col,row))
    {
        c = _at(col-_col, row-_row);
        if (c == 'P' || c == '_' || c == 'O')
            return NAME;
    }
    return backName(col,row);
}

//increase iterator
template <char NAME ,char A, char B>
void tile<NAME ,A,B,' '>::next()
{
    _iterator++;
    _rotation = (_rotation+1)%4;
    int tmp = _iterator/4;
    _col = tmp%4;
    _row = tmp/4;
}

//test if iterator is ended
template <char NAME ,char A, char B>
bool tile<NAME ,A,B,' '>::end()
{
    return _iterator >= 64;
}

//-------------------2 tile object protected method
//return tile at pos not relative to grid
//same magic as the three one
template <char NAME ,char A, char B>
char tile<NAME ,A,B,' '>::_at (int scol, int srow)
{
    switch (scol+srow*2+_rotation*10)
    {
    case 0:
    case 10:
    case 21:
    case 32:
        return A;
    case 1:
    case 12:
    case 20:
    case 30:
        return B;
    }
    return ' ';
}

/*__________________________________________________________*/

//create the six tiles object
typedef tile<'U','_','P','O'> U;
typedef tile<'V','P','_','O'> V;
typedef tile<'W','_','O','P'> W;
typedef tile<'X','P','P',' '> X;
typedef tile<'Y','P','O',' '> Y;
typedef tile<'Z','O','_','_'> Z;

#endif

