/*
   Copyright (C) 2009 - 2015 by Yurii Chernyi <terraninfo@terraninfo.net>
   Part of the Battle for Wesnoth Project http://www.wesnoth.org/

   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 of the License, or
   (at your option) any later version.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/

/**
 * @file
 * Managing the AIs lifecycle - headers
 * @todo 1.9 Refactor history handling and internal commands.
 * @todo 1.9 AI Interface command to clear the history.
 */

#ifndef AI_MANAGER_HPP_INCLUDED
#define AI_MANAGER_HPP_INCLUDED

#include "../config.hpp"
#include "../generic_event.hpp"

#include "game_info.hpp"

#include <stack>
#include <deque>


namespace ai {

class interface;

class side_context;
class readonly_context;
class readwrite_context;
class default_ai_context;
class ai_context;
class component;

class ai_composite;
typedef boost::shared_ptr<ai_composite> composite_ai_ptr;

/**
 * Base class that holds the AI and current AI parameters.
 * It is an implementation detail.
 * @todo 1.9 move it out of public view
 */
class holder{
public:
	holder(side_number side, const config &cfg);

	virtual ~holder();

	interface& get_ai_ref();

	const std::string describe_ai();

	void modify_ai_config_old( const config::const_child_itors &ai_parameters );

	config to_config() const;

	void modify_ai(const config& cfg);


	const std::string get_ai_overview();


	const std::string get_ai_structure();


	const std::string get_ai_identifier() const;

	component* get_component(component *root, const std::string &path); // Ai debug method

private:
	void init( side_number side );


	composite_ai_ptr ai_;
	side_context *side_context_;
	readonly_context *readonly_context_;
	readwrite_context *readwrite_context_;
	default_ai_context *default_ai_context_;
	side_number side_;
	config cfg_;
};

/**
 * AI Command History Item. It is an implementation detail
 */
class command_history_item{
public:

	command_history_item(int number, const std::string &command)
		: number_(number), command_(command)
	{}

	int get_number() const { return number_; }

	const std::string& get_command() const { return command_; }

private:
	int number_;
	std::string command_;

};

/**
 * Class that manages AIs for all sides and manages AI redeployment.
 * This class is responsible for managing the AI lifecycle
 * It can be accessed like this:   ai::manager::foo(...);
 */
class manager
{
public:

	// =======================================================================
	// CONSTANTS
	// =======================================================================

	static const size_t MAX_HISTORY_SIZE = 200;

	static const std::string AI_TYPE_COMPOSITE_AI;
	static const std::string AI_TYPE_SAMPLE_AI;
	static const std::string AI_TYPE_IDLE_AI;
	static const std::string AI_TYPE_FORMULA_AI;
	static const std::string AI_TYPE_DFOOL_AI;
	static const std::string AI_TYPE_AI2;
	static const std::string AI_TYPE_DEFAULT;


	// =======================================================================
	// LIFECYCLE
	// =======================================================================

	/**
	 * Sets AI information.
	 * @param info ai_information to be set.
	 */
	static void set_ai_info(const game_info& info);


	/**
	 * Clears AI information.
	 * Should be called in playsingle_controller 's destructor.
	 */
	static void clear_ai_info();


	/**
	 * Adds observer of game events.
	 * Should be called in playsingle_controller 's constructor.
	 */
	static void add_observer( events::observer* event_observer);


	/**
	 * Removes an observer of game events.
	 * Should be called in playsingle_controller 's destructor.
	 */
	static void remove_observer( events::observer* event_observer );


	/**
	 * Adds observer of game events except ai_user_interact event and ai_sync_network event
	 */
	static void add_gamestate_observer( events::observer* event_observer);


	/**
	 * Removes an observer of game events except ai_user_interact event and ai_sync_network event
	 */
	static void remove_gamestate_observer( events::observer* event_observer );


	/**
	 * Notifies all observers of 'ai_user_interact' event.
	 * Function which should be called frequently to allow the user to interact
	 * with the interface. This function will make sure that interaction
	 * doesn't occur too often, so there is no problem with calling it very
	 * regularly.
	 * @param force ignore if we already called this function recenlty, 
	 *    this is part of a hack related to mp_sync
	 */
	static void raise_user_interact(bool force= false);

	/**
	 * Notifies all observers of 'ai_sync_network' event.
	 * Basically a request from the AI to sync the network.
	 */
	static void raise_sync_network();


	/**
	 * Notifies all observers of 'ai_gamestate_changed' event.
	 */
	static void raise_gamestate_changed();


	/**
	 * Notifies all observers of 'ai_recruit_list_changed' event.
	 */
	static void raise_recruit_list_changed();


	/**
	 * Notifies all observers of 'ai_turn_started' event.
	 */
	static void raise_turn_started();


	/**
	 * Notifies all observers of 'ai_map_changed' event.
	 */
	static void raise_map_changed();


	/**
	 * Adds an observer of 'ai_map_changed' event.
	 */
	static void add_map_changed_observer( events::observer* event_observer );


	/**
	 * Adds an observer of 'ai_recruit_list_changed' event.
	 */
	static void add_recruit_list_changed_observer( events::observer* event_observer );


	/**
	 * Adds an observer of 'ai_turn_started' event.
	 */
	static void add_turn_started_observer( events::observer* event_observer );


	/**
	 * Deletes an observer of 'ai_map_changed' event.
	 */
	static void remove_map_changed_observer( events::observer* event_observer );



	/**
	 * Deletes an observer of 'ai_recruit_list_changed' event.
	 */
	static void remove_recruit_list_changed_observer( events::observer* event_observer );


	/**
	 * Deletes an observer of 'ai_turn_started' event.
	 */
	static void remove_turn_started_observer( events::observer* event_observer );


private:

	manager();


public:

	// =======================================================================
	// EVALUATION
	// =======================================================================

	/**
	 * Evaluates a string command using command AI.
	 * @note Running this command may invalidate references previously returned
	 *       by manager. Will intercept those commands which start with '!'
	 *       and '?', and will try to evaluate them as internal commands.
	 * @param side side number (1-based).
	 * @param str string to evaluate.
	 * @return string result of evaluation.
	 */
	static const std::string evaluate_command( side_number side, const std::string& str );


	// =======================================================================
	// ADD, CREATE AIs, OR LIST AI TYPES
	// =======================================================================

	/**
	 * Adds active AI for specified @a side from @a file.
	 * @note Running this command may invalidate references previously returned
	 *       by manager. AI is not initialized at this point.
	 * @param side side number (1-based, as in game_info).
	 * @param file file name, follows the usual WML convention.
	 * @param replace should new ai replace the current ai or 'be placed on top of it'.
	 * @return true if successful.
	 */
	static bool add_ai_for_side_from_file( side_number side, const std::string& file, bool replace = true );


	/**
	 * Adds active AI for specified @a side from @a cfg.
	 * @note Running this command may invalidate references previously returned
	 *       by manager. AI is not initialized at this point.
	 * @param side side number (1-based, as in game_info).
	 * @param cfg the config from which all ai parameters are to be read.
	 * @param replace should new ai replace the current ai or 'be placed on top of it'.
	 * @return true if successful.
	 */
	static bool add_ai_for_side_from_config(side_number side, const config &cfg, bool replace = true);


	/**
	 * Adds active AI for specified @a side from parameters.
	 * @note Running this command may invalidate references previously returned
	 *       by manager. AI is not initialized at this point.
	 * @param side side number (1-based, as in game_info).
	 * @param ai_algorithm_type type of AI algorithm to create.
	 * @param replace should new ai replace the current ai or 'be placed on top of it'.
	 * @return true if successful.
	 */
	static bool add_ai_for_side( side_number side, const std::string& ai_algorithm_type, bool replace = true);


	/**
	 * Returns a smart pointer to a new AI.
	 * @param ai_algorithm_type type of AI algorithm to create
	 * @param cfg a config of the ai
	 * @param ai_context context in which this ai is created
	 * @return the reference to the created AI
	 */
	static ai_ptr create_transient_ai( const std::string &ai_algorithm_type, const config &cfg, ai_context *ai_context);


	// =======================================================================
	// REMOVE
	// =======================================================================

	/**
	 * Removes top-level AI from @a side.
	 * @note Running this command may invalidate references previously returned
	 *       by manager.
	 * @param side side number (1-based, as in game_info).
	 */
	static void remove_ai_for_side( side_number side );


	/**
	 * Removes all AIs from @a side.
	 * @note Running this command may invalidate references previously returned
	 *       by manager.
	 * @param side side number (1-based, as in game_info).
	 */
	static void remove_all_ais_for_side( side_number side );


	/**
	 * Clears all the AIs.
	 * @note Running this command may invalidate references previously returned
	 *       by manager. For example, this is called from the destructor of
	 *       playsingle_controller. It is necessary to do this if any of the
	 *       info structures used by the AI goes out of scope.
	 */
	static void clear_ais();

	// =======================================================================
	// GET active AI parameters
	// =======================================================================


	/**
	 * Gets AI info for active AI of the given @a side.
	 * @param side side number (1-based).
	 * @return a reference to active AI info.
	 */
	static game_info& get_active_ai_info_for_side( side_number side );


	/**
	 * Gets AI Overview for active AI of the given @a side
	 * @param side side number (1-based)
	 * @return an ai overview
	 */
	static std::string get_active_ai_overview_for_side( side_number side);


	/**
	 * Gets AI Structure for active AI of the given @a side
	 * @param side side number (1-based)
	 * @return an ai structure
	 */
	static std::string get_active_ai_structure_for_side( side_number side);

	/**
	 * Gets AI algorithm identifier for active AI of the given @a side.
	 * @param side side number (1-based).
	 * @return ai identifier for the active AI
	 */
	static std::string get_active_ai_identifier_for_side( side_number side );

	/**
	 * Gets the active AI holder for debug purposes.
	 * Will only work in debug mode, otherwise returns a reference to an empty holder
	 * @param side side number(1-based)
	 * @return debug ? active holder : empty holder
	 */
	static ai::holder& get_active_ai_holder_for_side_dbg(side_number side);

	/**
	 * Gets AI config for active AI of the given @a side.
	 * @param side side number (1-based).
	 * @return a config object for the active AI
	 */
	static config to_config( side_number side );


	/**
	 * Gets global AI-game info
	 * @return a reference to the AI-game info.
	 */
	static game_info& get_ai_info();


	// =======================================================================
	// SET active AI parameters
	// =======================================================================


	/**
	 * Modifies AI parameters for active AI of the given @a side.
	 * This function is provided for backward-compatibility with [modify_side][ai]...[/ai][/modify_side]
	 * It can only add new facets to aspects
	 * @param side side_number (1-based, as in game_info).
	 * @param ai_parameters AI parameters to be modified.
	 */
	static void modify_active_ai_config_old_for_side ( side_number side, const config::const_child_itors &ai_parameters );



	/**
	 * Modifies AI parameters for active AI of the given @a side.
	 * This function is a backend for [modify_ai] tag
	 * @param side side_number (1-based, as in game_info).
	 * @param cfg - content of [modify_ai] tag
	 */

	static void modify_active_ai_for_side( ai::side_number side, const config &cfg );

	// =======================================================================
	// PROXY
	// =======================================================================

	/**
	 * Plays a turn for the specified side using its active AI.
	 * @param side side number (1-based, as in game_info).
	 */
	static void play_turn(side_number side);


private:

	typedef std::map< side_number, std::stack< holder > > AI_map_of_stacks;
	static AI_map_of_stacks ai_map_;
	static std::deque< command_history_item > history_;
	static long history_item_counter_;
	static game_info *ai_info_;

	static events::generic_event map_changed_;
	static events::generic_event recruit_list_changed_;
	static events::generic_event user_interact_;
	static events::generic_event sync_network_;
	static events::generic_event gamestate_changed_;
	static events::generic_event turn_started_;
	static int last_interact_;
	static int num_interact_;



	// =======================================================================
	// EVALUATION
	// =======================================================================

	/**
	 * Evaluates an internal manager command.
	 * @param side side number (1-based).
	 * @param str string to evaluate.
	 * @return string result of evaluation.
	 * @todo 1.9 rewrite this function to use a fai or lua parser.
	 */
	static const std::string internal_evaluate_command( side_number side, const std::string& str );

	/**
	 * Determines if the command should be intercepted and evaluated as internal command.
	 * @param str command string to check.
	 * @return true if the command should be intercepted and evaluated.
	 */
	static bool should_intercept( const std::string& str );

	// =======================================================================
	// AI STACKS
	// =======================================================================


	/**
	 * Gets the AI stack for the specified side, create it if it doesn't exist.
	 */
	static std::stack< holder >& get_or_create_ai_stack_for_side(side_number side);

	// =======================================================================
	// AI HOLDERS
	// =======================================================================


	/**
	 * Gets active holder for specified @a side.
	 */
	static holder& get_active_ai_holder_for_side( side_number side );

	// =======================================================================
	// AI POINTERS
	// =======================================================================

	/**
	 * Gets active AI for specified side.
	 * @note Running this command may invalidate references previously returned
	 *       by manager.
	 * @param side side number (1-based, as in game_info).
	 * @return a reference to the active AI.
	 * @note This reference may become invalid after specific manager operations.
	 */
	static interface& get_active_ai_for_side( side_number side );


};

} //end of namespace ai

#endif
