#ifndef _NETWORK_CMD_INGAME_H_
#define _NETWORK_CMD_INGAME_H_

#include "network_cmd.h"
#include "pwd_hash.h"
#include "../simworld.h"
#include "../tpl/slist_tpl.h"
#include "../utils/plainstring.h"
#include "koord3d.h"

class memory_rw_t;
class packet_t;
class spieler_t;
class werkzeug_t;

/**
 * nwc_gameinfo_t
 * @from-client: client wants map info
 *		server sends nwc_gameinfo_t to sender
 * @from-server:
 *		@data len of gameinfo
 *		client processes this in network_connect
 */
class nwc_gameinfo_t : public network_command_t {
public:
	nwc_gameinfo_t() : network_command_t(NWC_GAMEINFO) { len = 0; }
	virtual bool execute(karte_t *);
	virtual void rdwr();
	virtual const char* get_name() { return "nwc_gameinfo_t";}
	uint32 client_id;
	uint32 len;
};

/**
 * nwc_nick_t
 * @from-client: client sends new nickname,
 *               server checks if nickname is already taken,
 *               and generates a default one if this is the case
 * @from-server: server sends the checked nickname back to the client
 */
class nwc_nick_t : public network_command_t {
public:
	nwc_nick_t(const char* nick=NULL)
	: network_command_t(NWC_NICK), nickname(nick) {  }

	virtual bool execute(karte_t *);
	virtual void rdwr();
	virtual const char* get_name() { return "nwc_nick_t";}
	plainstring nickname;

	enum { WELCOME, CHANGE_NICK, FAREWELL};
	/**
	 * Server-side nickname related stuff:
	 * what = WELCOME     .. new player joined: send welcome message
	 * what = CHANGE_NICK .. change nickname: in socket_list per client, send new nick back to client, tell others as well
	 * what = FAREWELL    .. player has left: send message
	 */
	static void server_tools(karte_t *welt, uint32 client_id, uint8 what, const char* nick);

private:
	nwc_nick_t(const nwc_nick_t&);
	nwc_nick_t& operator=(const nwc_nick_t&);
};

/**
 * nwc_chat_t
 * @from-client: client sends chat message to server
 *               server logs message and sends it to all clients
 * @from-server: server sends a chat message for display on the client
 * @author Timothy Baldock <tb@entropy.me.uk>
 */
class nwc_chat_t : public network_command_t {
public:
	nwc_chat_t (const char* msg = NULL, sint8 pn = -1, const char* cn = NULL, const char* dn = NULL)
	: network_command_t(NWC_CHAT), message(msg), player_nr(pn), clientname(cn), destination(dn) {}

	virtual bool execute (karte_t *);
	virtual void rdwr ();
	virtual const char* get_name() { return "nwc_chat_t";}
	void add_message (karte_t*) const;

	plainstring message;            // Message text
	sint8 player_nr;                // Company number message was sent as
	plainstring clientname;	        // Name of client message is from
	plainstring destination;        // Client to send message to (NULL for all)

private:
	nwc_chat_t(const nwc_chat_t&);
	nwc_chat_t& operator=(const nwc_chat_t&);
};

/**
 * nwc_join_t
 * @from-client: client wants to join the server
 *		server sends nwc_join_t to sender, nwc_sync_t to all clients
 * @from-server:
 *		@data answer == 1 (if joining now is ok)
 *		@data client_id
 *		client ignores the following nwc_sync_t, waits for nwc_ready_t
 */
class nwc_join_t : public nwc_nick_t {
public:
	nwc_join_t(const char* nick=NULL)
	: nwc_nick_t(nick), client_id(0), answer(0) { id = NWC_JOIN; }

	virtual bool execute(karte_t *);
	virtual void rdwr();
	virtual const char* get_name() { return "nwc_join_t";}
	uint32 client_id;
	uint8 answer;

	/**
	 * this clients is in the process of joining
	 */
	static SOCKET pending_join_client;

	static bool is_pending() { return pending_join_client != INVALID_SOCKET; }
private:
	nwc_join_t(const nwc_join_t&);
	nwc_join_t& operator=(const nwc_join_t&);
};


/**
 * nwc_ready_t
 * @from-client:
 *		@data sync_steps at which client will continue
 *		client paused, waits for unpause
 * @from-server:
 *		data is resent to client
 *		map_counter to identify network_commands
 *		unpause client
 */
class nwc_ready_t : public network_command_t {
public:
	nwc_ready_t() : network_command_t(NWC_READY), sync_step(0), map_counter(0) { }
	nwc_ready_t(uint32 sync_step_, uint32 map_counter_, const checklist_t &checklist_) : network_command_t(NWC_READY), sync_step(sync_step_), map_counter(map_counter_), checklist(checklist_) { }
	virtual bool execute(karte_t *);
	virtual void rdwr();
	virtual const char* get_name() { return "nwc_ready_t";}
	uint32 sync_step;
	uint32 map_counter;
	checklist_t checklist;

	static void append_map_counter(uint32 map_counter_);
	static void clear_map_counters();
private:
	static vector_tpl<uint32>all_map_counters;
};

/**
 * nwc_game_t
 * @from-server:
 *		@data len of savegame
 *		client processes this in network_connect
 */
class nwc_game_t : public network_command_t {
public:
	nwc_game_t(uint32 len_=0) : network_command_t(NWC_GAME), len(len_) {}
	virtual void rdwr();
	virtual const char* get_name() { return "nwc_game_t";}
	uint32 len;
};


/**
 * nwc_auth_player_t
 * @from-client: client sends password hash to unlock player / set player password
 *		 server sends nwc_auth_player_t to sender
 * @from-server:
 *		 information whether players are locked / unlocked
 */
class nwc_auth_player_t : public network_command_t {
public:
	nwc_auth_player_t() : network_command_t(NWC_AUTH_PLAYER), hash(), player_unlocked(0), player_nr(255)  { }
	nwc_auth_player_t(uint8 nr, const pwd_hash_t& hash_) : network_command_t(NWC_AUTH_PLAYER), hash(hash_), player_unlocked(0), player_nr(nr)  { }
	virtual bool execute(karte_t *);
	virtual void rdwr();
	virtual const char* get_name() { return "nwc_auth_player_t";}
	pwd_hash_t hash;
	uint16 player_unlocked;
	uint8  player_nr;

	/**
	 * sets unlocked flags for playing at server
	 */
	static void init_player_lock_server(karte_t *);
};

/**
 * commands that have to be executed at a certain sync_step
 */
class network_world_command_t : public network_command_t {
public:
	network_world_command_t() : network_command_t(), sync_step(0), map_counter(0) {};
	network_world_command_t(uint16 /*id*/, uint32 /*sync_step*/, uint32 /*map_counter*/);
	virtual void rdwr();
	virtual const char* get_name() { return "network_world_command_t";}
	// put it to the command queue
	virtual bool execute(karte_t *);
	// apply it to the world
	virtual void do_command(karte_t*) {}
	uint32 get_sync_step() const { return sync_step; }
	uint32 get_map_counter() const { return map_counter; }
	// ignore events that lie in the past?
	// if false: any cmd with sync_step < world->sync_step forces network disconnect
	virtual bool ignore_old_events() const { return false;}
	// for sorted data structures
	bool operator <= (network_world_command_t c) const { return sync_step <= c.sync_step; }
	static bool cmp(network_world_command_t *nwc1, network_world_command_t *nwc2) { return nwc1->get_sync_step() <= nwc2->get_sync_step(); }
protected:
	uint32 sync_step; // when this has to be executed
	uint32 map_counter; // cmd comes from world at this stage
	// TODO: uint16 sub_step to have an order within one step
};

/**
 * nwc_sync_t
 * @from-server:
 *		@data client_id this client wants to receive the game
 *		@data new_map_counter new map counter for the new world after game reloading
 *		clients: pause game, save, load, wait for nwc_ready_t command to unpause
 *		server: pause game, save, load, send game to client, send nwc_ready_t command to client
 */
class nwc_sync_t : public network_world_command_t {
public:
	nwc_sync_t() : network_world_command_t(NWC_SYNC, 0, 0), client_id(0), new_map_counter(0) {};
	nwc_sync_t(uint32 sync_steps, uint32 map_counter, uint32 send_to_client, uint32 _new_map_counter) : network_world_command_t(NWC_SYNC, sync_steps, map_counter), client_id(send_to_client), new_map_counter(_new_map_counter) { }
	virtual void rdwr();
	virtual void do_command(karte_t*);
	virtual const char* get_name() { return "nwc_sync_t"; }
	uint32 get_new_map_counter() const { return new_map_counter; }
private:
	uint32 client_id; // this client shall receive the game
	uint32 new_map_counter;	// map counter to be applied to the new world after game reloading
};

/**
 * nwc_check_t
 * @from-server:
 *		@data checklist random seed and quickstone next check entries at previous sync_step
 *		clients: check random seed, if check fails disconnect.
 *		the check is done in karte_t::interactive
 */
class nwc_check_t : public network_world_command_t {
public:
	nwc_check_t() : network_world_command_t(NWC_CHECK, 0, 0), server_sync_step(0) { }
	nwc_check_t(uint32 sync_steps, uint32 map_counter, const checklist_t &server_checklist_, uint32 server_sync_step_) : network_world_command_t(NWC_CHECK, sync_steps, map_counter), server_checklist(server_checklist_), server_sync_step(server_sync_step_) {};
	virtual void rdwr();
	virtual void do_command(karte_t*) { }
	virtual const char* get_name() { return "nwc_check_t"; }
	checklist_t server_checklist;
	uint32 server_sync_step;
	// no action required -> can be ignored if too old
	virtual bool ignore_old_events() const { return true; }
};

/**
 * commands that need to be executed at a certain syncstep
 * the command will be cloned at the server and broadcasted to all clients
 */
class network_broadcast_world_command_t : public network_world_command_t {
public:
	network_broadcast_world_command_t(uint16 id, uint32 sync_step=0, uint32 map_counter=0)
	: network_world_command_t(id, sync_step, map_counter), exec(false) { }

	virtual void rdwr();
	virtual bool execute(karte_t *);

	// prepare the command before it will be put in the command queue
	virtual void pre_execute() { }

	// clones the command to be broadcasted
	// all validity checks must be done here
	// it must return a new command
	// clone() must return NULL to indicate failure
	virtual network_broadcast_world_command_t* clone(karte_t *) = 0;

	bool is_from_initiator() const { return !exec; }
private:
	// at server: true if command needs to be put in command queue
	// it will be cloned and broadcasted otherwise
	bool exec;
};

/**
 * nwc_chg_player_t
 * 		commands that require special authentication checks: toggle freeplay, start AI player
 * @from-server:
 * 		@data cmd command to perform (see karte_t::change_player_tool)
 * 		@data player_nr affected player
 * 		@data param
 */
class nwc_chg_player_t : public network_broadcast_world_command_t {
public:
	nwc_chg_player_t() : network_broadcast_world_command_t(NWC_CHG_PLAYER, 0, 0) { }
	nwc_chg_player_t(uint32 sync_steps, uint32 map_counter, uint8 cmd_=255, uint8 player_nr_=255, uint16 param_=0)
	: network_broadcast_world_command_t(NWC_CHG_PLAYER, sync_steps, map_counter), cmd(cmd_), player_nr(player_nr_), param(param_) {};
	virtual void rdwr();
	virtual void do_command(karte_t*);
	// do some special checks
	virtual network_broadcast_world_command_t* clone(karte_t *);
	virtual const char* get_name() { return "nwc_chg_player_t"; }

	uint8 cmd;
	uint8 player_nr;
	uint16 param;
};

/**
 * nwc_tool_t
 * @from-client: client sends tool init/work
 * @from-server: server sends nwc_tool_t to all clients with step when tool has to be executed
 *		@data client_id (the client that launched the tool, sent by server)
 *		@data player_nr
 *		@data init (if true call init else work)
 *		@data wkz_id
 *		@data pos
 *		@data default_param
 *		@data exec (if true executes, else server sends it to clients)
 */
class nwc_tool_t : public network_broadcast_world_command_t {
public:
	// to detect desync we sent these infos always together (only valid for tools)
	checklist_t last_checklist;
	uint32 last_sync_step;

	nwc_tool_t();
	nwc_tool_t(spieler_t *sp, werkzeug_t *wkz, koord3d pos, uint32 sync_steps, uint32 map_counter, bool init);
	nwc_tool_t(const nwc_tool_t&);

	// messages are allowed to arrive at any time
	virtual bool ignore_old_events() const;

	virtual ~nwc_tool_t();

	virtual void rdwr();

	virtual void pre_execute();
	// clone performs authentication checks
	virtual network_broadcast_world_command_t* clone(karte_t *);

	// really executes it, here exec should be true
	virtual void do_command(karte_t*);
	virtual const char* get_name() { return "nwc_tool_t"; }
private:
	char *default_param;
	uint32 tool_client_id;
	uint16 wkz_id;
	koord3d pos;
	uint8 flags;
	uint8 player_nr;
	bool init;

	uint8 custom_data_buf[256];
	memory_rw_t *custom_data;

	// compare default_param's (NULL pointers allowed)
	// @return true if default_param are equal
	static bool cmp_default_param(const char *d1, const char *d2);

	/**
	 * contains tools of players at other clients:
	 *   for every player at every client we store the active tool in this node class
	 *
	 * the member variable default_param saves the default-parameter of the tool,
	 * i.e. wkz->default_param == default_param
	 *
	 * default_param has its own simple memory management
	 */
	class tool_node_t {
	private:
		plainstring default_param;
		werkzeug_t *wkz;

		void set_default_param(char const* param) { default_param = param; }

		void set_tool(werkzeug_t *wkz_);

		// we do not want to copy wkz
		tool_node_t(const tool_node_t&);
		tool_node_t& operator=(const tool_node_t&);

	public:
		uint32 client_id;
		uint8 player_id;

		tool_node_t() : wkz(NULL), client_id(0), player_id(255) {}
		tool_node_t(werkzeug_t *_wkz, uint8 _player_id, uint32 _client_id) : wkz(_wkz), client_id(_client_id), player_id(_player_id) {}

		// necessary to ensure that wkz->default_param
		// always points to default_param

		const char* get_default_param() const { return default_param;}

		werkzeug_t* get_tool() const { return wkz;}

		/**
		 * mimics void karte_t::local_set_werkzeug(werkzeug_t *, spieler_t *)
		 * deletes wkz_new if wkz_new->init() returns false and store is false
		 */
		void client_set_werkzeug(werkzeug_t * &wkz_new, const char* default_param_, bool store, karte_t*, spieler_t*);

		/**
		 * @return true if ids (player_id and client_id) of both tool_node_t's are equal
		 */
		inline bool operator == (const tool_node_t c) const { return client_id==c.client_id  &&  player_id==c.player_id; }
	};

	// static list of active tools for each pair (client_id, player_id)
	static vector_tpl<tool_node_t*> tool_list;
};

#endif
