/*
 * msg_handle.c
 * 
 * Copyright (C) 2006-2007  Josef Kufner <jk@myserver.cz>
 * 
 * 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; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <assert.h>
#include <string.h>
#include <stdlib.h>

#include "msg_handle.h"
#include "message.h"
#include "dirs.h"
#include "options.h"
#include "gui.h"
#include "draw.h"
#include "backend.h"
#include "playerlist.h"
#include "status.h"
#include "glade_support.h"

#define DEBUG 0
#include "debug.h"


void msg_handle_chat(MESSAGE *msg)
{
	char *str = NULL, *cmd = NULL, *sender = NULL;
	int is_remote;

	DBG_MSG(3, "chat_handle_message");
	/*MSG* C message_body
	 * 
	 * Chat
	 *
	 * Send or display normal chat message.
	 */
	if (message_get(msg, MESSAGE_STRING, &cmd, MESSAGE_STRING, &str, MESSAGE_STRING, &sender, MESSAGE_INTEGER, &is_remote, MESSAGE_END)) {
		gui_append_to_chat("%s %s: %s\n", is_remote ? "->":"<-", sender, str);
	}
}


void msg_handle_backend(MESSAGE *msg)
{
	char *data = NULL;
	
	assert(msg != NULL);

	if (backend_network && msg->dest == 'N') {
		data = message_to_string(msg, 1);
		backend_send(backend_network, data);
		free(data);
	}
	
	if (backend_game && msg->dest == 'G') {
		data = message_to_string(msg, 1);
		backend_send(backend_game, data);
		free(data);
	}
}


void msg_handle_draw(MESSAGE *msg)
{
	char *cmd = NULL;
	char *file, *s;
	int img_id, obj_id, x, y, h, w;
	
	assert(msg != NULL);

	message_get(msg, MESSAGE_STRING, &cmd, MESSAGE_END);

	if (cmd && cmd[0] == 'B') {
		switch (cmd[1]) {
			case 'i':
				switch (cmd[2]) {
					case 'l':
						/*MSG* Bil filename img_id
						 * 
						 * Board Image Load
						 * 
						 * Load image from file. To
						 * show image on board, object
						 * must be created (see 'Boa').
						 */
						if (message_get(msg, MESSAGE_SKIP,
								MESSAGE_STRING, &file,
								MESSAGE_INTEGER, &img_id,
								MESSAGE_END))
						{
							draw_image_load(option_get_string("main", "dir", "data"), file, img_id);
						}
						break;

					case 'r':
						/*MSG* Bir img_id
						 *
						 * Board image remove.
						 *
						 * Remove image. No objects should use this image.
						 */
						if (message_get(msg, MESSAGE_SKIP,
								MESSAGE_INTEGER, &img_id,
								MESSAGE_END))
						{
							draw_image_remove(img_id);
						}
						break;
				}
				break;

			case 'o':
				switch (cmd[2]) {
					case 'a':
						/*MSG* Boa obj_id img_id x y height width
						 * 
						 * Board Object Add
						 * 
						 * Add object at board. img_id is id of image
						 * loaded with Bil. If height and width is -1,
						 * then size is determined automaticaly.
						 */
						if (message_get(msg, MESSAGE_SKIP,
								MESSAGE_INTEGER, &obj_id,
								MESSAGE_INTEGER, &img_id,
								MESSAGE_INTEGER, &x,
								MESSAGE_INTEGER, &y,
								MESSAGE_INTEGER, &h,
								MESSAGE_INTEGER, &w,
								MESSAGE_END))
						{
							draw_object_add(obj_id, img_id, x, y, h, w);
						}
						break;

					case 'd':
						/*MSG* Bod obj_id
						 *
						 * Board Object Delete
						 *
						 * Remove object from board.
						 */
						if (message_get(msg, MESSAGE_SKIP,
								MESSAGE_INTEGER, &obj_id,
								MESSAGE_END))
						{
							draw_object_delete(obj_id);
						}
						break;

					case 'm':
						/*MSG* Bom obj_id x y
						 * 
						 * Board Object Move
						 *
						 * Move object to new [x, y]. Don't forget to update
						 * board with 'Bu'.
						 */
						if (message_get(msg, MESSAGE_SKIP,
								MESSAGE_INTEGER, &obj_id,
								MESSAGE_INTEGER, &x,
								MESSAGE_INTEGER, &y,
								MESSAGE_END))
						{
							draw_object_move(obj_id, x, y);
						}
						break;
				}
				break;

			case 's':
				switch (cmd[2]) {
					case 't':
						/*MSG* Bst text
						 * 
						 * Board Statusbar Text
						 * 
						 * Set statusbar text to 'text'.
						 */
						if (message_get(msg, MESSAGE_SKIP,
								MESSAGE_STRING, &s,
								MESSAGE_END))
						{
							gui_set_statusbar_text(s);
						}
						break;
						
						
					case 'c':
						/*MSG* Bsc is_visible icon text
						 *
						 * Board Statusbar Clock
						 *
						 * Set statusbar clock (secondary text at right). If 
						 * 'is_visible' is zero, clock part of statusbar is hidden.
						 * 'icon' is integer from 0 to 5; zero means none.
						 */
						if (message_get(msg, MESSAGE_SKIP,
								MESSAGE_INTEGER, &x,
								MESSAGE_INTEGER, &y,
								MESSAGE_STRING, &s,
								MESSAGE_END))
						{
							gui_set_statusbar_clock(x, y, "%s", s);
						}
						break;
						
					case 'p':
						/*MSG* Bp is_visible percentage text
						 *
						 * Board Statusbar Progresbar
						 *
						 * Set progresbar in statusbar. If 'is_visible' is zero,
						 * progressbar is hidden. Filled part is given in percents
						 * as 'percentage'.
						 */
						if (message_get(msg, MESSAGE_SKIP,
								MESSAGE_INTEGER, &x,
								MESSAGE_INTEGER, &y,
								MESSAGE_STRING, &s,
								MESSAGE_END))
						{
							gui_set_statusbar_progress(x != 0, y / 100., "%s", s);
						}
						break;
						
					case 's':
						/*MSG* Bss value
						 *
						 * Board Statusbar Semaphore
						 *
						 * Set semaphore lights. Lights are binary representation
						 * of value, e.g. 2 means green on and red off.
						 */
						if (message_get(msg, MESSAGE_SKIP,
								MESSAGE_INTEGER, &x,
								MESSAGE_END))
						{
							if (x >= 0)
								gui_set_semaphore(x & 3);
						}
						break;
				}
				break;
			case 'u':
				/*MSG* Bu
				 *
				 * Board Update
				 *
				 * Just redraw board.
				 */
				draw_all();
				break;
				
			case 'r':
				/*MSG* Br
				 *
				 * Board Remove
				 *
				 * Remove everything from board. All objects, all images.
				 */
				draw_remove_all();
				break;
		}
	}

}


void msg_handle_options(MESSAGE *msg)
{
	char *cmd = NULL, *cat = NULL, *mod = NULL, *opt = NULL, *val_str = NULL;
	int val_int = 0;
	MESSAGE *reply = NULL;
	
	assert(msg != NULL);

	message_get(msg, MESSAGE_STRING, &cmd, MESSAGE_END);
	
	if (cmd && cmd[0] == 'O') {
		switch(cmd[1]) {
			case 'u':
				/*MSG* Ou cat mod opt
				 *
				 * Option Unset
				 *
				 * Remove given option from database.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &cat,
						MESSAGE_STRING, &mod,
						MESSAGE_STRING, &opt,
						MESSAGE_END))
				{
					option_unset(cat, mod, opt);
				}
				break;

			case 'a':
				/*MSG* Oa cat mod opt value
				 *
				 * Option Add
				 *
				 * If given option does not exist, set it to 'value'. 
				 * This is good for loading default values.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &cat,
						MESSAGE_STRING, &mod,
						MESSAGE_STRING, &opt,
						MESSAGE_END)
					&& option_get_type(cat, mod, opt) != OPTION_NONE)
				{
					break;
				}
				
			case 's':
				/*MSG* Os cat mod opt value
				 *
				 * Option Set
				 *
				 * Set given option to 'value'. 'value' can be 
				 * integer or string.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &cat,
						MESSAGE_STRING, &mod,
						MESSAGE_STRING, &opt,
						MESSAGE_STRING, &val_str,
						MESSAGE_END))
				{
					option_set_string(cat, mod, opt, val_str);
				} else if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &cat,
						MESSAGE_STRING, &mod,
						MESSAGE_STRING, &opt,
						MESSAGE_INTEGER, &val_int,
						MESSAGE_END))
				{
					option_set_integer(cat, mod, opt, val_int);
				}
				break;
				
			case 'g':
				/*MSG* Og cat mod opt value
				 *
				 * Option Get
				 *
				 * Request for value of given option. Option
				 * database will reply with 'Or'.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &cat,
						MESSAGE_STRING, &mod,
						MESSAGE_STRING, &opt,
						MESSAGE_END))
				{
					reply = message_create('O', msg->src);
					/*MSG* Or cat mod opt value
					 *
					 * Option Reply
					 *
					 * Option database will send this message
					 * as answer to 'Og'. 'value' can be string or
					 * integer if option exists. If option is not found,
					 * 'value' parameter will be missing.
					 */
					switch (option_get_type(cat, mod, opt)) {
						case OPTION_STRING:
							message_append(reply, MESSAGE_STRING, "Or",
									MESSAGE_STRING, cat,
									MESSAGE_STRING, mod,
									MESSAGE_STRING, opt,
									MESSAGE_STRING, option_get_string(cat, mod, opt),
									MESSAGE_END);
							break;

						case OPTION_INTEGER:
							message_append(reply, MESSAGE_STRING, "Or",
									MESSAGE_STRING, cat,
									MESSAGE_STRING, mod,
									MESSAGE_STRING, opt,
									MESSAGE_INTEGER, option_get_integer(cat, mod, opt),
									MESSAGE_END);
							break;

						case OPTION_NONE:
							message_append(reply, MESSAGE_STRING, "Or",
									MESSAGE_STRING, cat,
									MESSAGE_STRING, mod,
									MESSAGE_STRING, opt,
									MESSAGE_END);
							break;
					}
					message_send(reply);
					message_destroy(reply);
				}
				break;

			case 'd':
				/*MSG* Odo category module window_title fields...
				 *
				 * Option Dialog Open
				 *
				 * Shows dialog with given fields. Number of 'fields'
				 * is unlimited, but there is no scrollbar in dialog.
				 *
				 * Type of fields is determined by first character of its label.
				 *
				 * See documentation for details.
				 */
				/*MSG* Odc update
				 *
				 * Option Dialog Close
				 *
				 * Option dialog has been closed. If 'update' is 1, ok button 
				 * has been pressed and configuration has been changed.
				 */
				if (cmd[2] == 'o')
					gui_config_dialog_from_message(msg, msg->src != 'O');
				break;
		}
	}
}


void msg_handle_playerlist(MESSAGE *msg)
{
	char *cmd = NULL, *nick, *address, *role, *score, *conn_state;
	MESSAGE *reply = NULL;
	int i;

	assert(msg != NULL);

	message_get(msg, MESSAGE_STRING, &cmd, MESSAGE_END);
	
	if (cmd && cmd[0] == 'P') {
		switch(cmd[1]) {
			case 'j':
				/*MSG* Pj address
				 *
				 * Player Join
				 *
				 * Player has joined to the game.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &address,
						MESSAGE_END))
				{
					playerlist_add(address);
					gui_append_to_chat(_("-- %s se připojil(a)\n"), address);
				}
				break;

			case 'p':
				/*MSG* Pp address
				 * 
				 * Player Part
				 *
				 * Player has left the game.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &address,
						MESSAGE_END))
				{
					playerlist_remove(address);
					gui_append_to_chat(_("-- %s se odpojil(a)\n"), address);
				}
				break;
				
			case 'i':
				/*MSG* Pi address nick role score conn_state
				 *
				 * Player Info
				 *
				 * Update player info. All values are strings.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &address,
						MESSAGE_STRING, &nick,
						MESSAGE_STRING, &role,
						MESSAGE_STRING, &score,
						MESSAGE_STRING, &conn_state,
						MESSAGE_END))
				{
					playerlist_update(address, nick, role, score, conn_state);
				}
				break;
				
			case 'n':
				/*MSG* Pn address nick
				 *
				 * Player Nick
				 *
				 * Update player's nickname.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &address,
						MESSAGE_STRING, &nick,
						MESSAGE_END))
				{
					playerlist_update(address, nick, NULL, NULL, NULL);
				}
				break;
				
			case 'r':
				/*MSG* Pn address role
				 *
				 * Player Role
				 *
				 * Update player role.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &address,
						MESSAGE_STRING, &role,
						MESSAGE_END))
				{
					playerlist_update(address, NULL, role, NULL, NULL);
				}
				break;
				
			case 's':
				/*MSG* Pn address score
				 *
				 * Player Score
				 *
				 * Update player's score.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &address,
						MESSAGE_STRING, &score,
						MESSAGE_END))
				{
					playerlist_update(address, NULL, NULL, score, NULL);
				}
				break;
				
			case 'c':
				/*MSG* Pn address conn_state
				 *
				 * Player Connection state
				 *
				 * Update player's connection state. It is just string shown
				 * in playerlist. Intended to show ping or something like that.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &address,
						MESSAGE_STRING, &conn_state,
						MESSAGE_END))
				{
					playerlist_update(address, NULL, NULL, NULL, conn_state);
				}
				break;

			case 'g':
				/*MSG* Pg address
				 *
				 * Player Get
				 *
				 * Request for player info. Player list will reply
				 * with 'Pi' message.
				 */
				if (message_get(msg, MESSAGE_SKIP,
						MESSAGE_STRING, &address,
						MESSAGE_END))
				{
					playerlist_get(address, &nick, &role, &score, &conn_state, NULL);
					reply = message_create('P', msg->src);
					message_append(reply, MESSAGE_STRING, "Pi",
							MESSAGE_STRING, address,
							MESSAGE_STRING, nick ? nick : "",
							MESSAGE_STRING, role ? role : "",
							MESSAGE_STRING, score ? score : "",
							MESSAGE_STRING, conn_state ? conn_state : "",
							MESSAGE_END);
					message_send(reply);
					message_destroy(reply);
				}
				break;
				
			case 'l':
				/*MSG* Pl
				 *
				 * Player List
				 *
				 * Request for info about all players in game. Player list will reply with
				 * seqence of messages 'Plb', 'Pi', 'Pi', ..., 'Ple'.
				 */
				
				/* begin */
				reply = message_create('P', msg->src);
				message_append(reply, MESSAGE_STRING, "Plb", MESSAGE_END);
				message_send(reply);
				message_destroy(reply);

				/* data */
				for (i = 0; playerlist_list(&i, &address, &nick, &role, &score, &conn_state, NULL); i++) {
					if (address) {
						reply = message_create('P', msg->src);
						message_append(reply, MESSAGE_STRING, "Pi",
								MESSAGE_STRING, address,
								MESSAGE_STRING, nick ? nick : "",
								MESSAGE_STRING, role ? role : "",
								MESSAGE_STRING, score ? score : "",
								MESSAGE_STRING, conn_state ? conn_state : "",
								MESSAGE_END);
						message_send(reply);
						message_destroy(reply);
					}
				}

				/* end */
				reply = message_create('P', msg->src);
				message_append(reply, MESSAGE_STRING, "Ple", MESSAGE_END);
				message_send(reply);
				message_destroy(reply);

				break;
		}
	}
}


void msg_handle_session(MESSAGE *msg)
{
	char *cmd = NULL, *str = NULL, *addr = NULL, *role = NULL;
	
	assert(msg != NULL);

	message_get(msg, MESSAGE_STRING, &cmd, MESSAGE_END);
	
	if (cmd && cmd[0] == 'S') {
		DBG_MSG(3, "msg_handle_session: cmd = \"%s\"", cmd);
		switch (cmd[1]) {
			case 'o': /* online/offline */
				switch (cmd[2]) {
					case 'n':	/* online */
						/*MSG* Son
						 *
						 * Status ONline
						 *
						 * Status notification.
						 */
						if (get_status() == S_CONNECTING) {
							switch_to_status(S_CONNECTED);
						}
						break;

					case 'f':	/* offline */
						/*MSG* Sof
						 *
						 * Status OFfline
						 * 
						 * Status notification.
						 */
						if (get_status() != S_DISCONNECTING && get_status() != S_EXITING) {
							switch_to_status(S_CONNECTING);
						}
						break;
				}
				break;

			case 'r': /* role list */
				/*MSG* Sr roles
				 *
				 * Session Role
				 *
				 * List of valid player roles.
				 */
				message_get_next(msg, 1, MESSAGE_SKIP, MESSAGE_END);
				gui_role_clear();
				while (message_get_next(msg, 0, MESSAGE_STRING, &str, MESSAGE_END)) {
					gui_role_add(str);
				}
				break;

			case 'p': /* player */
				switch (cmd[2]) {
					case 'a': /* ack */
						/*MSG* Spa human_readable_description
						 *
						 * Session Player Ack
						 */
						if (message_get(msg, MESSAGE_SKIP, MESSAGE_SKIP, MESSAGE_STRING, &addr, MESSAGE_END)) {
							playerlist_update_ack(addr, 1);
						}
						break;
						
					case 'e': /* error */
						/*MSG* Spe human_readable_description
						 *
						 * Session Player Error
						 */
						if (message_get(msg, MESSAGE_SKIP, MESSAGE_SKIP, MESSAGE_STRING, &addr, MESSAGE_END)) {
							playerlist_update_ack(addr, -2);
						}
						break;

					case 'r': /* role */
						/*MSG* Spr human_readable_description role address 
						 *
						 * Session Player Role
						 */
						if (message_get(msg, MESSAGE_SKIP, MESSAGE_SKIP, MESSAGE_STRING, &role, MESSAGE_STRING, &addr, MESSAGE_END)) {
							playerlist_update(addr, NULL, role, NULL, NULL);
							if (get_status() == S_LOADED) {
								gtk_widget_set_sensitive(lookup_widget(main_window, "game_start_button"), TRUE);
								gtk_widget_set_sensitive(lookup_widget(main_window, "game_role_combobox"), TRUE);
							}
						}
						break;

					case 'g': /* select game */
						/*MSG* Spg human_readable_description game_name
						 *
						 * Session Player Game
						 */
						if (get_status() == S_CONNECTED && message_get(msg, MESSAGE_SKIP, MESSAGE_SKIP, MESSAGE_STRING, &str, MESSAGE_END)) {
							option_set_string("main", "game", "backend", str);
							switch_to_status(S_LOADING);
						}
						break;

					case 'b': /* start game */
						/*MSG* Spb human_readable_description
						 *
						 * Session Player Begin
						 */
						if (message_get(msg, MESSAGE_SKIP, MESSAGE_SKIP, MESSAGE_STRING, &addr, MESSAGE_END)) {
							playerlist_update_ack(addr, 2);
							playerlist_get(addr, &str, NULL, NULL, NULL, NULL);
							gui_append_to_chat(_("-- %s zahajuje hru\n"), str && str[0] ? str : addr);

							if (playerlist_have_all_acks(2)) {
								switch_to_status(S_GAME_PLAYING);
							}
						}
						break;
						
					case 'o': /* over -- hrac uz nehraje */
						/*MSG* Spo human_readable_description
						 *
						 * Session Player Over
						 */
						if (message_get(msg, MESSAGE_SKIP, MESSAGE_SKIP, MESSAGE_STRING, &addr, MESSAGE_END)) {
							playerlist_update_ack(addr, -1);
							playerlist_get(addr, &str, NULL, NULL, NULL, NULL);
							gui_append_to_chat(_("-- %s končí\n"), str && str[0] ? str : addr);

							if (playerlist_have_all_acks(-1)) {
								switch_to_status(S_CONNECTED);
							}
						}
						break;

					case 't': /* turn -- forward only */
						/*MSG* Spt human_readable_description data
						 *
						 * Session Player Turn
						 */
						if (msg->src == 'N') {
							message_edit(msg, 'S', 'G');
						} else if (msg->src == 'G') {
							message_edit(msg, 'S', 'N');
						}
						message_send(msg);
						break;

				}
				break;
				
			case 'g': /* game */
				switch (cmd[2]) {
					case 'l': /* loaded */
						/*MSG* Sgl
						 *
						 * Session Game Loaded
						 */
						DBG_MSG(1, "Sgl: stat = %d", get_status());
						if (get_status() == S_LOADING) {
							switch_to_status(S_LOADED);
						}
						break;
						
					case 'a': /* accept */
						/*MSG* Sga
						 *
						 * Session Game Accept
						 */
						DBG_MSG(1, "Sga: stat = %d", get_status());
						if (get_status() == S_GAME_CHECK) {
							switch_to_status(S_GAME_STARTING);
						}
						break;

					case 'r': /* reject */
						/*MSG* Sgr
						 *
						 * Session Game Reject
						 */
						DBG_MSG(1, "Sgr: stat = %d", get_status());
						if (get_status() == S_GAME_CHECK) {
							switch_to_status(S_LOADED);
						}
						{
							GtkWidget *d;
							d = gtk_message_dialog_new(GTK_WINDOW(main_window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
									GTK_BUTTONS_OK, _("Ještě nelze zahájit hru"));
							gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(d),
									_("Herní modul odmítl zahájení hry. Nejspíš nejsou rozděleny všechny"
									  " role nebo jsou rozděleny špatně."));
							gtk_dialog_run(GTK_DIALOG(d));
							gtk_widget_destroy(d);
						}
						break;

				}
				break;

		}
	}
}


void msg_handle_network_profiles(MESSAGE *msg)
{
	char *cmd = NULL;
	int update = 0;
	
	assert(msg != NULL);

	/* only update window */
	if (message_get(msg, MESSAGE_STRING, &cmd, MESSAGE_INTEGER, &update, MESSAGE_END)) {
		if (strcmp(cmd, "Odc") == 0 && update && dialog_network_profiles) {
			DBG_MSG(3, "opdating network profiles dialog...");
			gui_update_network_profiles();
			DBG_MSG(3, "opdating network profiles dialog... done");
		}
	}
}


void msg_handle_test(MESSAGE *msg)
{
	char *str, *p_msg_str = NULL;
	MESSAGE *m;
	
	DBG_MSG(1, "test");
	str = message_to_string(msg, 0);
	DBG_MSG(1, "string: %s", str);

	m = message_from_string('T', 'T', str, &p_msg_str);
	free(str);

	str = message_to_string(msg, 0);
	DBG_MSG(1, "string: %s", str);

	free(str);
	message_destroy(m);
	
	return;
}

/* vim:set encoding=utf8: */
