#include "all.h"
#include <pwd.h>
#include <unistd.h>
#include <errno.h>
extern errno;
/*
 * This is the major pieces of code for docking. Other related
 * sources include:
 * --> docking.h			// Define the protocol with docking applet.
 * --> dock_net.c			// Helper function for making sockets
 * 
 * Two major varialbes, dsock, and csock is declared in globals.h.
 */

// We need two socket fd, coz unix domain socket is half duplex.
// To enable two way communication, we need two socket

// Size of sname must be >= 20
// Type must be either 'd' or 'c'
// -- c stands for chinput socket name
// -- d stands for docking socket name
// Return the length of address, or return NULL if error
static int chinput_dock_get_sockname (char *sname, int size, int type)
{
	char *user_name;
	struct passwd *pwdrec = NULL;

	pwdrec = getpwuid (getuid ());
	user_name = pwdrec->pw_name;
//	printf ("user_name is %s\n", user_name);

	*sname='\0';
	strcat (sname, HZSOCK_DIR);
	if (type == 'c')
	{
		strcat (sname, HZSOCK_CNAME);
		size -= strlen(HZSOCK_DIR)-strlen(HZSOCK_CNAME)-strlen(HZSOCK_SUFFIX)-1;
	}
	else if (type == 'd')
	{
		strcat (sname, HZSOCK_DNAME);
		size -= strlen(HZSOCK_DIR)-strlen(HZSOCK_CNAME)-strlen(HZSOCK_SUFFIX)-1;
	}
	else
		return (int)NULL;
	
	strncat (sname, user_name, size);
	strcat (sname, HZSOCK_SUFFIX);
//	printf ("Chinput server socket name is %s\n", sname);

	return (int)strlen(sname);
}

// Some procedure for a dock which is going to be close.
static void chinput_dock_cleanup (void)
{
	close (dsock);
	dsock = -1;
	docking_live = 0;
}

static int chinput_dock_send_init (int sock)
{
	DockMessage to_mess;
	int write_cnt;

	memset (&to_mess, 0, sizeof (DockMessage));
	to_mess.message_type = HZ_DOCK_INIT_ACK;

	write_cnt = write (sock, &to_mess, sizeof (DockMessage));
	if (write_cnt < 0)
	{
		perror ("chinput_dock_send_init () >> write ()");
		return (1);
	}
	else if (write_cnt == 0)
	{
		perror ("chinput_dock_send_init () >> write () >> warning, write_cnt == 0");
	}
	return 0;
}

static int chinput_dock_send_imlist (int sock)
{
	DockMessage to_mess;
	int write_cnt;
	int im_record_cnt = 0;

	memset (&to_mess, 0, sizeof (DockMessage));
	to_mess.message_type = HZ_DOCK_REPLY_LIST;

	// Get and pack list for docking, 
	// MAX_IMES = 60, hence, chinputime has 60 elements. But we want 10 only.
	// Ignore the remaining. Get the first 10, which is favourite=1
	while (im_record_cnt < DOCK_MAX_IM_NUM && im_record_cnt < index_ime)
	{
		to_mess.im_record[im_record_cnt].im_index = im_record_cnt;
		strncpy (to_mess.im_record[im_record_cnt].im_en_name,  
			chinputime[im_record_cnt].name, 20);
		im_record_cnt ++;
	}
	
	// If english??
	if (map_mode == 0)
		to_mess.im_index = -1;	
	// Or chinese ??
	else
		to_mess.im_index = cur_inputmethod;

	to_mess.im_record_count = im_record_cnt;
	
	write_cnt = write (sock, &to_mess, sizeof (DockMessage));
	if (write_cnt < 0)
	{
		perror ("chinput_dock_send_imlist () >> write ()");
		return (1);
	}
	else if (write_cnt == 0)
	{
		perror ("chinput_dock_send_imlist () >> write () >> warning, write_cnt == 0");
	}
	return 0;
}

static int chinput_dock_change (int ime_index)
{
	printf ("Change IM request got, index is %d.\n", ime_index);
	// Change to EN
	if (map_mode == 1 && ime_index == -1)
	{
		chinput_hide_window ();
	}
	else if (map_mode == 0 && ime_index >= 0)
	{
		chinput_switch_inputmethod (ime_index);
		chinput_popup_window ();
	}
	else if (map_mode == 1 && ime_index >= 0)
	{
		chinput_switch_inputmethod (ime_index);
	}
	return 0;
}

void chinput_dock_change_notify (void)
{
	DockMessage to_mess;
	int write_cnt;
	int sock = dsock;
	int new_index;

	// Check if docking is up and available.
	if (docking_live == 0)
		return;

//	printf ("Notifying docking, ");

	// Check IM
	// Just hide input win, and using english input...
	if (map_mode == 0)
		new_index = -1;
	else
		new_index = cur_inputmethod;
	printf ("Notify Docking from chinput, new im index is #%d.\n", new_index);
	
	memset (&to_mess, 0, sizeof (DockMessage));
	to_mess.message_type = HZ_DOCK_CHANGE_IM;
	to_mess.im_index = new_index;
	// Send it
	write_cnt = write (sock, &to_mess, sizeof (DockMessage));
	if (write_cnt < 0)
		perror ("chinput_dock_change_notify () >> write ()");
	else if (write_cnt == 0)
		perror ("chinput_dock_change_notify () >> write () >> warning, write_cnt == 0");

	if (fsync (sock) == -1)
	{
		perror ("chinput_dock_change_notify () >> fsync ()");
		switch (errno)
		{
			case EBADF:
				printf ("EBADF\n");
				break;
			case EROFS:
			case EINVAL:
				printf ("EROFS / EINVAL\n");
				break;
			case EIO:
				printf ("EIO\n");
				break;
			default :
				printf ("Unknow errno\n");
				break;
		}
	}
}

void chinput_dock_callback_dispatch (int sig_num,
		siginfo_t *sig_info, void *misc)
{
	DockMessage from_mess;
	int read_cnt;

	if (sig_info->si_signo != SIGIO)
		return;

	// Read csock, we may need to double check th siginfo.si_fd
	read_cnt = read (csock, &from_mess, sizeof (DockMessage));
	if (read_cnt < 0)
	{
		perror ("chinput_dock_callback_dispatch () >> read ()");
		return;
	}
	else if (read_cnt == 0)
	{
		perror ("chinput_dock_callback_dispatch () >> read () >> warning, read_cnt == 0");
		return;
	}
	else
	{
		char dsock_name[HZSOCKNAME_MAX];
#if 0
		char timestr[64];
		time_t sys_time;
		struct tm *time_struct;

		sys_time = time (NULL);
		time_struct = localtime (&sys_time);
		strftime (timestr, 64, "%T", time_struct);

		printf ("Message #%d got at : %s\n", from_mess.message_type, timestr);
#endif
		switch (from_mess.message_type)
		{
			case HZ_DOCK_INIT:
//				printf ("Init.\n");
				if ( ! chinput_dock_get_sockname
						(dsock_name, HZSOCKNAME_MAX, 'd'))
					chinput_util_print_sys_msg
							("Warning: Cannot create docking socket name.");

				if (chinput_net_connect_peer (&dsock, dsock_name))
					perror ("error in connecting docking socket");

				if (chinput_dock_send_init (dsock))
					perror ("error in send initAck to docking socket");

				docking_live = 1;
				return;

			case HZ_DOCK_QUIT:
//				printf ("Quit.\n");
				chinput_dock_cleanup ();
				return;

			case HZ_DOCK_REQUEST_LIST:
//				printf ("Request List.\n");
				if (chinput_dock_send_imlist (dsock))
					chinput_dock_cleanup ();
				return;

			case HZ_DOCK_CHANGE_IM:
//				printf ("Change IM.\n");
				// Change IM, no ACK needed.
				if (chinput_dock_change (from_mess.im_index))
					chinput_dock_cleanup ();
				return;

			case HZ_DOCK_SYNC_QUERY:
				// Not Implement
				return;

			default:
				return;
		}
	}
}

void chinput_dock_init (void)
{
	char csock_name[HZSOCKNAME_MAX];
	
	// Initialize the sock, sock == -1 implies, not connected
	csock = -1;
	dsock = -1;

	if (!chinput_dock_get_sockname (csock_name, HZSOCKNAME_MAX, 'c'))
		chinput_util_print_sys_msg ("Warning: Cannot create server socket name.");

	// Create csock, chinput socket.
	if (chinput_net_create_socket (&csock, csock_name))
		chinput_net_clear_socket ("Socket create fail.\n", csock, dsock);

	if (chinput_net_set_sock_attr (csock))
		chinput_net_clear_socket ("Set socket attribute fail.\n", csock, dsock);

	if (chinput_net_set_signal_handler (SIGIO, &chinput_dock_callback_dispatch))
		chinput_net_clear_socket ("Set signal handler fail.\n", csock, dsock);
}
