// **********************************************************************
//                            OpenAlchemist
//                        ---------------------
//
//  File        : Player.h
//  Description : 
//  Author      : Guillaume Delhumeau <guillaume.delhumeau@gmail.com>
//  License     : GNU General Public License 2 or higher
//
// **********************************************************************

#ifndef _PLAYER_H_
#define _PLAYER_H_

#include <SDL.h>
#include "SDL_CL_Sprite.h"

#include "Piece.h"
#include "Board.h"
#include "KeyboardKey.h"
#include "ProgressBar.h"
#include "CombosPainter.h"

enum GameMode{
	GAME_MODE_PLAYING,
	GAME_MODE_FALLING_AND_CREATING,
	GAME_MODE_DESTROYING,
	GAME_MODE_GAME_OVER
};

/**
* Implements the player, keys events for going left, right & falling
* and contains his board.
*/
class Player{

public:
	
	/** Constructor	*/
	Player(int player = 1);

	/** Destructor */
	virtual ~Player();

	/** Add statistics listener */
	void add_listener(Statistics *listener);

	/** Load GFX */
	void load_gfx(SDL_Renderer *gc, std::string basename);

	/** Unload GFX */
	void unload_gfx();

	/** Checking key events	*/
	void events();    

	/** Draw player (board and playable pieces)	*/
	void draw(SDL_Renderer *gc);

	/** Make a new party */
	void new_game();

	/** Terminate the party	*/
	void term_game();

	/** Update playable pieces and board */
	void update();  

	/** Get score */
	inline unsigned int get_score(){ return _board.get_score(); }

	/** Returning if undo is available */
	bool is_undo_available();

	/** Undoing last action */
	void undo();  

	/** Returns if the game is over	*/
	bool is_game_over();

	bool is_ready() const { return _is_ready; }

	/** Give up	*/
	void give_up();  

	std::string get_element_set() const { return _element_set; }

	GameMode get_game_mode() { return _game_mode; }

	/** Get number of visible pieces */
	int get_visible_pieces();

	void set_element_set(const std::string &element_set);

	void set_ready(bool val) { _is_ready = val; }

	/** Change angle of playable pieces	*/
	void change_angle();

	/** Move playable pieces to the left */
	void move_left();

	/** Move playable pieces to the right */
	void move_right();

	/** Fall playable pieces */
	void fall();

	/** Returns if the player is human	*/
	virtual bool is_human() = 0;

	bool get_key_up() { return _key_change_angle.get(); }
	bool get_key_left() { return _key_left.get(); }
	bool get_key_right() { return _key_right.get(); }
	bool get_key_down() { return _key_down.get(); }
	bool get_key_action() { return _key_action.get(); }

protected:

	/** X Position */
	float _x;

	/** Angle */
	float _angle;

	/** Angle required (for changing) */
	int _aimed_angle;

	/** Current position (in cols) */
	int _position;

	/** Old position (to undo - in cols) */
	int _old_position;

	/** 1 if angle = 90 or 180 */
	int _position_bis;

	/** Old position bis to undo */
	int _old_position_bis;

	/** To know if the position bis is due to right position */
	bool _is_forced_bis;

	/** To know if pieces are moving too the left or to the right or not */
	bool _is_placed;

	/** Game Mode */
	GameMode _game_mode;

	/** Save if the game mode has changed */
	bool _game_mode_changed;

	/** Sound played */
	bool _play_destroying_sound;

	/** Manual mode (to stop game mode succession - for DemoPlayer)	*/
	bool _manual_mode;

	/** One of the next piece (displaying in top) */
	Piece* _p_next_piece1;

	/** One of the next piece (displaying in top) */
	Piece* _p_next_piece2;

	/** Next pieces ID */
	int _next_next_piece1, _next_next_piece2;

	/** X Position for next items */
	int _next_left;

	/** Y Position for next items */
	int _next_top;

	/** Determines if the next items should be displayed vertical or horizontal */
	bool _next_vertical;

	/** One of the current pieces */
	Piece* _p_current_piece1;

	/** One of the current pieces */
	Piece* _p_current_piece2;

	/** Rayon of circle which contains playable pieces */
	int _current_pieces_r;

	/** Element set if separate from skin */
	std::string _element_set;

	/** Contains the normal pieces sprites */
	CL_Sprite _pieces_normal[NUMBER_OF_PIECES];

	/** Contains the appearing pieces sprites */
	CL_Sprite _pieces_appearing[NUMBER_OF_PIECES];

	/** Contains the disappearing pieces sprites */
	CL_Sprite _pieces_disappearing[NUMBER_OF_PIECES];

	/** Contains the mini pieces sprites */
	CL_Sprite _pieces_mini[NUMBER_OF_PIECES];

	/** Contains the hidden pieces sprites */
	CL_Sprite _pieces_hidden[NUMBER_OF_PIECES - 3];

	/** X Coord of pieces preview */
	int _pieces_preview_x[NUMBER_OF_PIECES];

	/** Y Coord if pieces preview */
	int _pieces_preview_y[NUMBER_OF_PIECES];

	/** Board game */
	Board _board;

	CL_Sprite _game_over;
	int _game_over_top;
	int _game_over_left;

	/** Key for playing	*/
	KeyboardKey _key_change_angle;

	/** Key for playing	*/
	KeyboardKey _key_left;

	/** Key for playing	*/
	KeyboardKey _key_right;

	/** Key for playing	*/
	KeyboardKey _key_falling;

	/** Key for menu	*/
	KeyboardKey _key_down;

	/** Key for menu	*/
	KeyboardKey _key_action;

	/** True if player press fall button */
	bool _is_falling_requested;

	/** Combos Painter */
	CombosPainter _combos_painter;

	/** Progress Bar */
	ProgressBar _progress_bar;

	/** Undo elements */
	int _undo_position;
	int _undo_position_bis;
	int _undo_piece1_number;
	int _undo_piece2_number;
	int _undo_angle;
	bool _undo_possible;

	/** True if the player is ready to start */
	bool _is_ready;

	void _load_element_gfx(SDL_Renderer *gc, zip_t *element_zip);

	/** Update when game_mode = playing	*/
	void _update_playing();

	/** Update when game_mode = falling & creating */
	void _update_falling_and_creating();

	/** Update when game_mode = destroying */
	void _update_destroying();

	/** Prepare game to play (after a fall)	*/
	void _prepare_to_play();

};

#endif
