/*
 * Skype plugin for libpurple/Pidgin/Adium
 * Written by: Eion Robb <eionrobb@gmail.com>
 *
 * This plugin uses the Skype API to show your contacts in libpurple, and send/receive
 * chat messages.
 * It requires the Skype program to be running.
 *
 * 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 3 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef INCLUDED_LIBSKYPE_C 
#	error	"Don't compile this file directly.  Just compile libskype.c instead."
#endif

#include <glib.h>
#include <errno.h>
#include <string.h>
//#include <sys/ioctl.h>

#ifdef _WIN32
#define fsync(fd) _commit(fd)
#endif

static guint input_timeout;
static gint source_sock = -1;
static gboolean connected = FALSE;
static gboolean in_progress = FALSE;
static gchar etb_string[2] = {23, 0};


void
read_function_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	int len;
	gchar response[3096];
	static GString *response_string = NULL;
	gchar *reply;
	gchar **reply_pieces;
	int i;

	len = read(source, response, sizeof(response)-1);
	if (len < 0)
	{
		if (errno != EAGAIN && errno != EWOULDBLOCK)
		{
			skype_disconnect();
		}
		return;
	}
	
	if (len == sizeof(response)-1)
	{
		if (response_string == NULL)
			response_string = g_string_new_len(response, len);
		else
			response_string = g_string_append_len(response_string, response, len);
	}
	else
	{
		if (response_string)
		{
			if (len > 0)
				response_string = g_string_append_len(response_string, response, len);
			reply = g_string_free(response_string, FALSE);
			response_string = NULL;
		}
		else if (len)
			reply = g_strndup(response, len);
		else
			return;
		reply = g_strstrip(reply);
		reply_pieces = g_strsplit(reply, etb_string, -1);
		g_free(reply);
		for (i=0; reply_pieces[i+1]; i++)
		{
			reply = reply_pieces[i];
			if (g_str_equal(reply, "LOGIN"))
			{
				connected = TRUE;
				in_progress = FALSE;
				skype_debug_info("skype", "Received: LOGIN\n");
			} else {
				g_thread_create((GThreadFunc)skype_message_received, g_strdup(reply), FALSE, NULL);
			}
		}
		//check that we received part of a message
		if (strlen(reply_pieces[i]))
		{
			skype_debug_info("skype", "Last piece: '%s'\n", reply_pieces[i]);
			response_string = g_string_new(reply_pieces[i]);
		}
		g_strfreev(reply_pieces);
	}
}

gpointer
skype_read_thread(gpointer data)
{
	while(connected || in_progress)
	{
		read_function_cb(data, source_sock, PURPLE_INPUT_READ);
		g_thread_yield();
		sleep(1);
	}
	return data;
}

void
connect_function(gpointer data, gint source, const gchar *error_message)
{
	gchar *loginmsg;
	PurpleAccount *acct = skype_get_account(NULL);
	
	if (error_message)
	{
		in_progress = FALSE;
		g_thread_create((GThreadFunc)skype_message_received, g_strdup("CONNSTATUS LOGGEDOUT"), FALSE, NULL);
		return;
	}
	source_sock = source;
	
	loginmsg = g_strdup_printf("LOGIN %s %s", acct->username, acct->password);
	send_message(loginmsg);
	skype_debug_info("skype", "Sending: 'LOGIN {username} {password}'\n");
	g_free(loginmsg);
	
	g_thread_create((GThreadFunc)skype_read_thread, NULL, FALSE, NULL);
}

static gboolean
skype_connect()
{
	return connected;
}

static void
skype_disconnect()
{
	if (!connected)
		return;
	
	send_message("QUIT");

	connected = FALSE;
	close(source_sock);
	source_sock = -1;
	purple_input_remove(input_timeout);
	in_progress = FALSE;
}

static void
send_message(const char* message)
{
	int message_num;
	char *error_return;
	char *temp;
	int msglen = -1;
	int len = 0;
	
	if (message)
	{
		msglen = strlen(message) + 1;
		temp = g_strdup_printf("%s%c", message, 23);
		len = write(source_sock, temp, msglen);
		fsync(source_sock);
		g_free(temp);
	}
	
	if (len != msglen)
	{
		//There was an error
		if (message[0] == '#')
		{
			//And we're expecting a response
			sscanf(message, "#%d ", &message_num);
			error_return = g_strdup_printf("#%d ERROR NETWORK", message_num);
			g_thread_create((GThreadFunc)skype_message_received, (void *)error_return, FALSE, NULL);
		}
	}
}

static void
hide_skype()
{
	//don't need to since SILENT_MODE ON works
	return;
}

gboolean
connection_timeout(gpointer data)
{
	in_progress = FALSE;
	return FALSE;
}

static gboolean
exec_skype()
{
	if (!connected && !in_progress)
	{
		in_progress = TRUE;
		PurpleAccount *acct = skype_get_account(NULL);
		purple_proxy_connect(acct->gc, acct, purple_account_get_string(acct, "host", "skype.robbmob.com"), purple_account_get_int(acct, "port", 5000), connect_function, acct);
		g_thread_create((GThreadFunc)skype_read_thread, acct, FALSE, NULL);
		purple_timeout_add_seconds(10, connection_timeout, acct);
	}
	return TRUE;
}

static gboolean
is_skype_running()
{
	return connected || in_progress;
}
