#include <iostream>
#include <sstream>
#include <ctime>
#include <SFML/Graphics.hpp>

using namespace sf;
using namespace std;

#define GETCOL( X ) ((X - 241) / 65)
#define GETROW( Y ) ((Y - 41) / 65)
#define OVERGEM(mX, mY) (mX > 241 && mX < 241 + 65 * 8 && mY > 36 && mY < 36 + 65 * 8)
#define GETPOS(i, j) Vector2f(241 + i*65, 41 + j * 65 )

class AnyPang;

class State {
public: 
	virtual void event(Event e, AnyPang *anypang);
	virtual void updateModel(AnyPang *anypang) {}

	virtual void updateViewBefore(AnyPang *anypang);
	virtual void updateView(AnyPang *anypang) {}
	virtual void updateViewAfter(AnyPang *anypang);
};

class StateReady: public State { void event(Event, AnyPang *); };
class StateFirstChosen: public State {  void event(Event, AnyPang *); void updateView(AnyPang *);  };
/*
class StateSwapAnim: public State { void event(Event, AnyPang *); };
class StateMatchAnim: public State { void event(Event, AnyPang *); void updateModel(AnyPang *); void updateView(AnyPang *); };
class StateDropAnim: public State { void event(Event, AnyPang *); void updateModel(AnyPang *); void updateView(AnyPang *); };
class StateGenAnim: public State { void event(Event, AnyPang *); void updateModel(AnyPang *); void updateView(AnyPang *); };
class StateTimeout: public State { void event(Event, AnyPang *); void updateModel(AnyPang *); void updateView(AnyPang *); };
*/

class AnyPang {

	static const int GEMCOUNT = 4;
	static const int GEMWIDTH = 65;
	static const int BOARD_OFFSET_X = 241;
	static const int BOARD_OFFSET_Y = 36;

	enum Gem { gemEmpty, gemBlue, gemGreen, gemRed, gemWhite };

	// State
	State *currentState;

	// Model
	Gem board[8][8];
	int chosenX;
	int chosenY;

	// View
	RenderWindow *window;

	Font fnMenu;
	Sprite spBackground;
	Sprite spCursor;
	Text ttExit;
	Text ttScore;
	Texture gemtex[GEMCOUNT+1];
	Sprite spSelector;
	Sprite spChosen;

	StateReady stateReady;
	StateFirstChosen stateFirstChosen;
/*
	StateSwapAnim stateSwapAnim;
	StateMatchAnim stateMatchAnim;
	StateDropAnim stateDropAnim;
	StateGenAnim stateGenAnim;
	StateTimeout stateTimeout;
*/
	void initModel();
	void initView();
	void initState();
	bool removeMatch();
	void dropGems();
	void fillGems(bool count_score=true);

public:
	AnyPang() { initModel(); initView(); initState(); }
	void run() { while( window->isOpen() ) {
				Event e;
				while( window->pollEvent(e) ) 
					currentState->event(e, this);
				window->clear();
				currentState->updateModel(this);
				currentState->updateViewBefore(this);
				currentState->updateView(this);
				currentState->updateViewAfter(this);
				window->display();
			}
	}
	friend class State;
	friend class StateReady;
	friend class StateFirstChosen;
};

void AnyPang::initModel() {
	
	srand((unsigned int) time(0) );

	for( int i=0; i<8; i++ )
		for( int j=0; j<8; j++ ) 
			board[i][j] = Gem(rand() % GEMCOUNT + 1);

	while( removeMatch() ) {
		dropGems();
		fillGems(false);
	}
}

void AnyPang::initView() {

	window = new RenderWindow(sf::VideoMode(800, 600), "Anypang");
	window->setFramerateLimit(60);

	// Load background image
	Texture *txBackground = new Texture;
	txBackground->loadFromFile("resources/Background.png");
	spBackground.setTexture(*txBackground);

	// Load cursor image
	Texture *txCursor = new Texture;
	txCursor->loadFromFile("resources/handCursor.png");
	spCursor.setTexture(*txCursor);
	spCursor.setOrigin(3,3);
	window->setMouseCursorVisible(false);

	// Load font
	fnMenu.loadFromFile("resources/fNormal.ttf");

	ttExit.setFont(fnMenu);
	ttExit.setCharacterSize(30);
	ttExit.setColor(sf::Color::Red);
	ttExit.setPosition(20, 538);
	ttExit.setString("Exit");

	ttScore.setFont(fnMenu);
	ttScore.setColor(Color::Yellow);
	ttScore.setPosition(20, 490);
	ttScore.setString("0");

	gemtex[gemBlue].loadFromFile("resources/gemBlue.png");
	gemtex[gemGreen].loadFromFile("resources/gemGreen.png");
	gemtex[gemRed].loadFromFile("resources/gemRed.png");
	gemtex[gemWhite].loadFromFile("resources/gemWhite.png");

	Texture *txSelector = new Texture;
	txSelector->loadFromFile("resources/selector.png");
	spSelector.setTexture(*txSelector);

	spChosen.setTexture(*txSelector);
	spChosen.setColor(Color::Red);
}

void AnyPang::initState() {
	currentState = &stateReady;
}

bool AnyPang::removeMatch ()
{
	int k;
	bool removed = false;
	Gem copy[8][8];
	memcpy( copy, board, 64*sizeof(Gem) );

	// First, we check each row (horizontal)
    for(int y = 0; y < 8; ++y){
        for(int x = 0; x < 6; ++x){

			int match = 0;

            for(k = x+1; k < 8; ++k){
                if(board[x][y] == board[k][y] &&
                   board[x][y] != gemEmpty){
                    match++;
                }else{
                    break;
                }
            }

            if(match >= 2){
                for(int z = x; z <= x + match; z++ ) {
					board[z][y] = gemEmpty;
					removed = true;
				}
            }

            x = k - 1;
        }   
    }

    for(int x = 0; x < 8; ++x){
        for(int y = 0; y < 6; ++y){

            int match = 0;

            for(k = y + 1; k < 8; ++k){
                if(copy[x][y] == copy[x][k] &&
                   copy[x][y] != gemEmpty){
                    match++;
                }else{
                    break;
                }
            }

            if(match >= 2){
                for(int z = y; z <= y + match; z++ ) {
					board[x][z] = gemEmpty;
					removed = true;
				}
            }

            y = k - 1;
        }
    }
	return removed;
}

void AnyPang::dropGems()
{
	for(int x = 0; x < 8; ++x){
		int skip = 0;
        for(int y = 7; y >= 0; --y){
			if( board[x][y] == gemEmpty ) {
				skip++;
				continue;
			} else if( skip > 0 ) {
				// fall skip 
				board[x][y+skip] = board[x][y];
				board[x][y] = gemEmpty;
			}
		}
	}

}

void AnyPang::fillGems(bool count_score)
{
	int removedBlockCnt = 0;
	for(int x=0; x < 8; x++ ) {
		for( int y=0; y < 8; y++ ) {
			if( board[x][y] == gemEmpty ) {
				board[x][y] = (Gem)(rand() % GEMCOUNT + 1);
				removedBlockCnt++;
			}
		}
	}
//	if( count_score) score += removedBlockCnt * 5 * multiplier++;
}

void State::event(Event e, AnyPang *anypang) 
{
	if (e.type == Event::Closed)
	{
		anypang->window->close();
	}
	else if (e.type == Event::MouseButtonPressed && e.mouseButton.button == Mouse::Left)
	{
		if( anypang->ttExit.getGlobalBounds().contains(e.mouseButton.x, e.mouseButton.y ) )
			anypang->window->close();
		/*if( OVERGEM( e.mouseButton.x, e.mouseButton.y ) ) {
			anypang->spChosen.setPosition( GETPOS( GETCOL( e.mouseButton.x ), GETROW( e.mouseButton.y ) ) );
			anypang->currentState = &anypang->stateFirstChosen;
		}
		*/
	}
	else if (e.type == Event::MouseMoved && OVERGEM( e.mouseMove.x, e.mouseMove.y ) ) {
		anypang->spSelector.setPosition( GETPOS( GETCOL(e.mouseMove.x), GETROW(e.mouseMove.y) ) );
	}
}

void State::updateViewBefore( AnyPang *anypang ) {
	anypang->spCursor.setPosition(static_cast<sf::Vector2f>(Mouse::getPosition(*anypang->window)));
	anypang->window->draw( anypang->spBackground );
	anypang->window->draw( anypang->ttExit );
	anypang->window->draw( anypang->ttScore );

	for( int i=0; i<8; i++ )
		for( int j=0; j<8; j++ ) {
			if( anypang->board[i][j] == AnyPang::gemEmpty )
				continue;
			Sprite agem( anypang->gemtex[anypang->board[i][j]] );
			agem.setPosition( GETPOS(i, j) );
			anypang->window->draw( agem );
		}
}

void State::updateViewAfter( AnyPang *anypang ) {
	anypang->window->draw( anypang->spSelector );
	anypang->window->draw( anypang->spCursor );
}

void StateReady::event(Event e, AnyPang *anypang) 
{
	State::event(e, anypang );

	if (e.type == Event::MouseButtonPressed && e.mouseButton.button == Mouse::Left)
	{
		if( OVERGEM( e.mouseButton.x, e.mouseButton.y ) ) {
			anypang->chosenX = GETCOL( e.mouseButton.x );
			anypang->chosenY = GETROW( e.mouseButton.y );

			anypang->spChosen.setPosition( GETPOS( anypang->chosenX, anypang->chosenY ) );
			anypang->currentState = &anypang->stateFirstChosen;
		}
	}
}

void StateFirstChosen::updateView(AnyPang *a) 
{ 
	a->window->draw( a->spChosen ); 
}

void StateFirstChosen::event(Event e, AnyPang *anypang) 
{
	State::event(e, anypang );

	if (e.type == Event::MouseButtonPressed && e.mouseButton.button == Mouse::Left)
	{
		if( OVERGEM( e.mouseButton.x, e.mouseButton.y ) ) {
			int secondX = GETCOL(e.mouseButton.x);
			int secondY = GETROW(e.mouseButton.y);

			if( abs( secondX - anypang->chosenX ) + abs(secondY - anypang->chosenY) == 1 ) {
				AnyPang::Gem tmp;
				tmp = anypang->board[anypang->chosenX][anypang->chosenY];
				anypang->board[anypang->chosenX][anypang->chosenY] = anypang->board[secondX][secondY];
				anypang->board[secondX][secondY] = tmp;
				while( anypang->removeMatch() ) {
					anypang->dropGems();
					anypang->fillGems();
				}
				anypang->currentState = &anypang->stateReady;
			}
			else
				anypang->currentState = &anypang->stateReady;

		}
	}
}

int g_multiplier = 1;
int g_score = 0;
int g_count2score = false;

int main() {
	AnyPang anypang;
	anypang.run();

	return 0;
}
/*
*/
#if 0 
int main()
{
	int score = 0;
	RenderWindow window(sf::VideoMode(800, 600), "Bejeweled");
	//Settign the framerate limit to 60 FPS
	window.setFramerateLimit(60);

	// Load background image
	Texture txBackground;
	if(txBackground.loadFromFile("resources/Background.png") == false)
	{
		return -1;
	}

	// Create background Sprite
	Sprite spBackground;
	spBackground.setTexture(txBackground);

	// Load cursor image
	Texture txCursor;
	if(txCursor.loadFromFile("resources/handCursor.png") == false)
	{
		return -1;
	}

	// Create cursor Sprite
	Sprite spCursor;
	spCursor.setTexture(txCursor);
	spCursor.setOrigin(3,3);
	window.setMouseCursorVisible(false);

	// Load font
	Font fnMenu;
	if( fnMenu.loadFromFile("resources/fNormal.ttf") == false )
		return -1;

	Text ttExit;
	ttExit.setFont(fnMenu);
	ttExit.setCharacterSize(30);
	ttExit.setColor(sf::Color::Red);
	ttExit.setPosition(20, 538);
	ttExit.setString("Exit");

	Text ttScore;
	ttScore.setFont(fnMenu);
	ttScore.setColor(Color::Yellow);
	ttScore.setPosition(20, 490);
	ttScore.setString("0");

	Texture txBlue;
	txBlue.loadFromFile("resources/gemBlue.png");
	Sprite spBlue(txBlue);
	spBlue.setPosition(100,100);

	Texture txGreen;
	txGreen.loadFromFile("resources/gemGreen.png");
	Sprite spGreen(txGreen);
	spGreen.setPosition(100,100);

	Texture txRed;
	txRed.loadFromFile("resources/gemRed.png");
	Sprite spRed(txRed);
	spRed.setPosition(100,100);

	Texture txWhite;
	txWhite.loadFromFile("resources/gemWhite.png");
	Sprite spWhite(txWhite);
	spWhite.setPosition(100,100);
	
	Texture txOrange;
	txOrange.loadFromFile("resources/gemOrange.png");
	Sprite spOrange(txOrange);
	spOrange.setPosition(100,100);

	Texture txPurple;
	txPurple.loadFromFile("resources/gemPurple.png");
	Sprite spPurple(txPurple);
	spPurple.setPosition(100,100);

	Texture txYellow;
	txYellow.loadFromFile("resources/gemYellow.png");
	Sprite spYellow(txYellow);
	spYellow.setPosition(100,100);

	Texture txEmpty;

	//srand((unsigned int)time(0));
	srand(100);
	int tileWidth = txBlue.getSize().x;
	int tileHeight = txBlue.getSize().y;
	//Sprite gems[5] = { spEmpty, spBlue, spGreen, spRed, spWhite };
	Texture gemtex[5] = { txEmpty, txBlue, txGreen, txRed, txWhite };

	const int BOARD_OFFSET_X = 241;
	const int BOARD_OFFSET_Y = 36;

	Gem board[8][8];
	for( int i=0; i<8; i++ )
		for( int j=0; j<8; j++ ) {
			board[i][j] = Gem(rand()%4+1);
		}

	Texture txSelector;
	txSelector.loadFromFile("resources/selector.png");
	Sprite spSelector(txSelector);

	Sprite spChosen(txSelector);
	spChosen.setColor(Color::Red);



	int chosenX=-1, chosenY=-1, secondX, secondY;
	bool checkMatch = true;
	bool countToScore = false;

	while( window.isOpen() ) 
	{
		Event e;
		while (window.pollEvent(e))
		{
			//Event type is window closed
			if (e.type == Event::Closed)
			{
				window.close();
			}
			else if (e.type == Event::MouseButtonPressed)
			{
				if (e.mouseButton.button == Mouse::Left)
				{
					if( ttExit.getGlobalBounds().contains(e.mouseButton.x, e.mouseButton.y ) )
						window.close();

					if( overGem(e.mouseButton.x, e.mouseButton.y) ) {
						if( chosenX < 0 ) {
							chosenX = GETCOL(e.mouseButton.x);
							chosenY = GETROW(e.mouseButton.y);
						}
						else {
							secondX = GETCOL(e.mouseButton.x);
							secondY = GETROW(e.mouseButton.y);
							if( abs( secondX - chosenX ) + abs(secondY - chosenY) == 1 ) {
								Gem tmp;
								tmp = board[chosenX][chosenY];
								board[chosenX][chosenY] = board[secondX][secondY];
								board[secondX][secondY] = tmp;
								checkMatch = true;
							}
							chosenX = chosenY = secondX = secondY = -1;
						}
					}

				}
			}
			else if (e.type == Event::KeyPressed ) {
				if( e.key.code == Keyboard::Space )
					checkMatch = true;
			}
		}
		
		if(checkMatch) {
			printBoard( "before remveMatch", board );
			if( removeMatch( board ) == true ) {
				printBoard( "after remveMatch", board );
				dropFill( board );
				printBoard( "after dropFill", board );
				newFill( board );
				printBoard( "after newFill", board );
			} else {
				cout << "## NO MATCH FOUND" << endl;
				checkMatch = false;
				g_multiplier = 1;
			}
		}

		window.clear();

		window.draw(spBackground);
		spCursor.setPosition(static_cast<sf::Vector2f>(Mouse::getPosition(window)));
		window.draw(ttExit);
		ttScore.setString( static_cast<ostringstream*>( &(ostringstream() << g_score) )->str() );
		window.draw(ttScore);
		

		for( int i=0; i<8; i++ )
			for( int j=0; j<8; j++ ) {
				if( board[i][j] == gemEmpty )
					continue;
				Sprite agem( gemtex[board[i][j]] );
				agem.setPosition( GETPOS(i, j) );
				window.draw( agem );
				if( GETCOL( Mouse::getPosition(window).x ) == i && GETROW( Mouse::getPosition(window).y ) == j ) {
					spSelector.setPosition( GETPOS( i, j ) );
					window.draw( spSelector );
				}
				if( i==chosenX && j==chosenY ) {
					spChosen.setPosition( GETPOS( i, j ) );
					window.draw( spChosen );
				}
			}

		window.draw(spCursor);

		window.display();
	}
	return 0;

}

#endif
