/**
*   Copyright (C) 2007 by Marcelo Jorge Vieira (metal) <metal@alucinados.com>
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*
*  This library 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
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
*
* Public License can be found at http://www.gnu.org/copyleft/gpl.html
*
*
* @author Thadeu Cascardo <cascardo@minaslivre.org>
* @author Marcelo Jorge Vieira (metal) <metal@alucinados.com>
*
*/
 
#include <gtk/gtk.h>
#include "velha.h"
 
GdkPixbuf *pixbuf_O;
GdkPixbuf *pixbuf_X;
GtkWidget *statusbar;
 
GtkListStore* store;
 
GString *strbuffer;
GIOChannel *channel;
 
gboolean started;
gboolean played;
gboolean listed;
 
gint game;
gint lastpos;
 
GtkWidget *velha[9];
 
void
button_pressed (GtkButton *button, gpointer data)
{
	gint position;
	char *str;
	GString *str2;
 
	position = GPOINTER_TO_INT (data);
	str = g_strdup_printf ("Pressed button %d", position);
	gtk_statusbar_push (GTK_STATUSBAR (statusbar),
	gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
	g_free (str);
	str2 = g_string_sized_new (BUFSIZ);
	g_string_printf (str2, "PLAY %d\r\n", position);
	write (g_io_channel_unix_get_fd (channel), str2->str, str2->len);
	g_string_free (str2, TRUE);
	lastpos = position;
	played = 1;
}
 
void
start_clicked (GtkButton *button, gpointer data)
{
	write (g_io_channel_unix_get_fd (channel), "START\r\n", 7);
	started = 1;
}
 
void
join_clicked (GtkButton *button, gpointer data)
{
	GtkTreeIter iter;
	GString *str;
 
	if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (data), &iter))
		return;
	gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (data)), &iter, 1, &game, -1);
	str = g_string_sized_new (BUFSIZ);
	g_string_printf (str, "JOIN %d\r\n", game);
	write (g_io_channel_unix_get_fd (channel), str->str, str->len);
	g_string_free (str, TRUE);
}
 
GtkWidget *
my_combo_new ()
{
	GtkWidget *combobox;
	GtkCellRenderer* renderer;
 
	store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
	combobox = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
	renderer = gtk_cell_renderer_text_new ();
	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, TRUE);
	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer, "text", 0, NULL);
 
	return combobox;
 
}
 
void
ui_init ()
{
	GtkWidget *window;
	GtkWidget *vbox;
	GtkWidget *hbox;
	GtkWidget *button;
	GtkWidget *entry;
 
	int i;
	int j;
 
	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title (GTK_WINDOW (window), "velha");
	gtk_container_set_border_width (GTK_CONTAINER (window), 10);
	vbox = gtk_vbox_new (FALSE, 5);
	gtk_container_add (GTK_CONTAINER (window), vbox);
	pixbuf_O = gdk_pixbuf_new_from_file_at_size ("velha_o.png", 100, 100, NULL);
	pixbuf_X = gdk_pixbuf_new_from_file_at_size ("velha_x.png", 100, 100, NULL);
 
	hbox = gtk_hbox_new (TRUE, 5);
	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 5);
 
	button = gtk_button_new_with_label ("Start new game");
	gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 5);
	g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (start_clicked), NULL);
 
	entry = my_combo_new ();
	gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
 
	button = gtk_button_new_with_label ("Join existing game");
	gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
	g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (join_clicked), entry);
 
	for (i = 0; i < 3; i++)
	{
		hbox = gtk_hbox_new (TRUE, 5);
		gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 5);
		for (j = 0; j < 3; j++)
		{
			velha[INDEX(i,j)] = gtk_button_new ();
			gtk_container_add (GTK_CONTAINER (hbox), velha[INDEX(i,j)]);
			gtk_widget_set_usize (velha[INDEX(i,j)], 120, 120);
			g_signal_connect (G_OBJECT (velha[INDEX(i,j)]), "clicked", G_CALLBACK (button_pressed), GINT_TO_POINTER (INDEX(i,j)));
		}
	}
 
	statusbar = gtk_statusbar_new ();
	gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 0);
	g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
	gtk_widget_show_all (window);
}
 
void
myfar (char *buffer, int len)
{
	if (started)
	{
		/* Waits for game number */
		char *end;
		char *str;
 
		buffer[len - 1] = 0;
		errno = 0;
		game = strtol (buffer, &end, 0);
		if (end == buffer || errno != 0 || game < 0 || game > G_MAXINT)
		{
			str = g_strdup_printf ("Error starting game");
			gtk_statusbar_push (GTK_STATUSBAR (statusbar),
			gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
			g_free (str);
			return;
		}
		str = g_strdup_printf ("Started game %d", game);
		gtk_statusbar_push (GTK_STATUSBAR (statusbar),
		gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
		g_free (str);
		started = 0;
	}
	else if (played)
	{
		char *end;
		char *str;
		int status;
 
		buffer[len - 1] = 0;
		errno = 0;
		status = strtol (buffer, &end, 0);
		if (end == buffer || errno != 0 || status < 0 || status > G_MAXINT)
		{
			str = g_strdup_printf ("Error: broken server");
			gtk_statusbar_push (GTK_STATUSBAR (statusbar),
			gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
			g_free (str);
			return;
		}
		if (status != 200)
		{
			str = g_strdup_printf ("Error playing: %s", end);
			gtk_statusbar_push (GTK_STATUSBAR (statusbar),
			gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
			g_free (str);
		}
		else
		{
			GtkWidget *image;
			str = g_strdup_printf ("Played position: %d", lastpos);
			gtk_statusbar_push (GTK_STATUSBAR (statusbar),
			gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
			g_free (str);
			image = gtk_image_new_from_pixbuf (pixbuf_O);
			gtk_button_set_image (GTK_BUTTON (velha[lastpos]), image);
		}
		played = 0;
	}
	else if (g_ascii_strncasecmp (buffer, "PLAY", 4) == 0)
	{
		/* Opponent has played */
		char *p;
		char *end;
		char *str;
		int position;
 
		buffer[len - 1] = 0;
		p = buffer + sizeof ("PLAY");
		errno = 0;
		position = strtol (p, &end, 0);
		if (end == p || errno != 0 || position < 0 || position > 8)
		{
			str = g_strdup_printf ("Error: broken server");
			gtk_statusbar_push (GTK_STATUSBAR (statusbar),
			gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
			g_free (str);
			return;
		}
		else
		{
			GtkWidget *image;
 
			str = g_strdup_printf ("Opponent played position: %d", position);
			gtk_statusbar_push (GTK_STATUSBAR (statusbar),
			gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
			g_free (str);
			image = gtk_image_new_from_pixbuf (pixbuf_X);
			gtk_button_set_image (GTK_BUTTON (velha[position]), image);
		}
	}
	else if (listed && g_ascii_strncasecmp (buffer, "END", 3) == 0)
	{
		listed = 0;
	}
	else if (listed && g_ascii_strncasecmp (buffer, "START", 5) == 0)
	{
		/* Who is responsible for this stupid protocol? Do nothing! */
	}
	else if (listed)
	{
		char *end;
		char *str;
		gint mygame;
 
		buffer[len - 1] = 0;
		errno = 0;
		mygame = strtol (buffer, &end, 0);
		if (end == buffer || errno != 0 || mygame < 0 || mygame > G_MAXINT)
		{
			str = g_strdup_printf ("Error: broken server");
			gtk_statusbar_push (GTK_STATUSBAR (statusbar),
			gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
			g_free (str);
			return;
		}
		else
		{
			GtkTreeIter iter;
 
			str = g_strdup_printf ("Seen game: %d", mygame);
			gtk_statusbar_push (GTK_STATUSBAR (statusbar),
			gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
			g_free (str);
			str = g_strdup_printf ("%d", mygame);
			gtk_list_store_prepend (store, &iter);
			gtk_list_store_set (store, &iter, 0, str, 1, mygame, -1);
		}
	}
	else if (g_ascii_strncasecmp (buffer, "200 WIN", 7) == 0)
	{
		char *str;
 
		str = g_strdup_printf ("You win!");
		gtk_statusbar_push (GTK_STATUSBAR (statusbar),
		gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
		g_free (str);
	}
	else if (g_ascii_strncasecmp (buffer, "200 LOOSE", 9) == 0)
	{
		char *str;
 
		str = g_strdup_printf ("You loose!");
		gtk_statusbar_push (GTK_STATUSBAR (statusbar),
		gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
		g_free (str);
	}
	else if (g_ascii_strncasecmp (buffer, "403 GAME", 8) == 0)
	{
		char *str;
 
		str = g_strdup_printf ("Game already started! Go away!");
		gtk_statusbar_push (GTK_STATUSBAR (statusbar),
		gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
		g_free (str);
	}
	else if (g_ascii_strncasecmp (buffer, "200 TIE", 7) == 0)
	{
		char *str;
 
		str = g_strdup_printf ("Game tie!");
		gtk_statusbar_push (GTK_STATUSBAR (statusbar),
		gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), str), str);
		g_free (str);
	}
}
 
gboolean
myboo ()
{
	int n;
	char *p;
	char *buffer;
 
	for (n = 0, p = strbuffer->str; n < strbuffer->len; n++, p++)
	{
		if (*p == '\n' || (*p == '\r' && ++n < strbuffer->len && *(++p) == '\n'))
			break;
	}
	if (n == strbuffer->len)
	{
		//g_debug ("Time to learn how to count, cascardo.");
		return FALSE;
	}
	buffer = g_strndup (strbuffer->str, n + 1);
	g_string_erase (strbuffer, 0, n + 1);
	//g_debug ("Command %s received", buffer);
	myfar (buffer, n + 1);
	g_free (buffer);
	return TRUE;
}
 
gboolean
myread (GIOChannel *channel, GIOCondition cond, gpointer data)
{
	int fd;
	char buffer[BUFSIZ];
	int r;
 
	//g_debug ("New data");
	fd = g_io_channel_unix_get_fd (channel);
	r = read (fd, buffer, BUFSIZ);
	if ((r < 0 && errno != EAGAIN) || r == 0)
	{
		g_debug ("Error or finished connection.");
		g_io_channel_unref (channel);
		return FALSE;
	}
	g_string_append_len (strbuffer, buffer, r);
	while (myboo ());
	return TRUE;
}
 
void
net_init ()
{
	int fd;
	struct sockaddr_in saddr;
 
	fd = socket (PF_INET, SOCK_STREAM, 0);
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons (5555);
	saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
	connect (fd, (struct sockaddr*) &saddr, sizeof (struct sockaddr_in));
	channel = g_io_channel_unix_new (fd);
	g_io_channel_set_close_on_unref (channel, TRUE);
	g_io_channel_set_flags (channel, g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK, NULL);
	g_io_add_watch (channel, G_IO_IN, myread, NULL);
	strbuffer = g_string_sized_new (BUFSIZ);
	write (fd, "LIST\r\n", 6);
	listed = 1;
}
 
int
main (int argc, char **argv)
{
	gtk_init (&argc, &argv);
 
	ui_init ();
	net_init ();
 
	gtk_main ();
 
	return 0;
}