#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <arpa/inet.h>

typedef int SOCKET;
#define closesocket(s) close (s)
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1

#endif

#include <glib.h>
  
static guint source_id;
static GMutex *mutex;
static GCond *cond;
 
static gboolean
recv_cb (GIOChannel *source,
         GIOCondition condition,
         gpointer data)
{
  g_assert_not_reached ();
}

static gboolean
send_cb (GIOChannel *source,
         GIOCondition condition,
         gpointer data)
{
  gboolean is_second = (gboolean) data;
  GIOStatus status;
  gsize bytes_written;
  gchar c = 'T';

  status = g_io_channel_write_chars (source, &c, 1, &bytes_written, NULL);
  g_print ("%s send_cb write char, status: %d.\n",
           (is_second ? "Second" : "First"),
           status);

  g_print ("\n==> Removing source %d\n", source_id);
  g_source_remove (source_id);

  g_print ("\n==> Adding a IN|ERR watch\n");
  source_id = g_io_add_watch (source,
                               G_IO_IN | G_IO_ERR,
                               recv_cb,
                               NULL);
  g_print ("==> source %d\n", source_id);

  g_mutex_lock (mutex);
  g_cond_signal (cond);
  g_mutex_unlock (mutex);

  return (TRUE);
}

int
main (int argc, char **argv)
{
#ifdef _WIN32
  WSADATA wsa_data;
#endif

  SOCKET s;
  struct sockaddr_in addr;

  GIOChannel *channel;
  GMainLoop *loop;
  GThread *thread;

#ifdef _WIN32
  g_assert (WSAStartup(0x0202, &wsa_data) == 0);
#endif
  g_thread_init (NULL);

  mutex = g_mutex_new ();
  cond = g_cond_new ();

  s = socket (AF_INET, SOCK_STREAM, 0);
  g_assert (s != INVALID_SOCKET);

  addr.sin_family = AF_INET;
  addr.sin_port = htons (5555);
  addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);

  g_assert (connect (s, (struct sockaddr *) &addr, sizeof (addr)) != SOCKET_ERROR);

#ifdef _WIN32
  channel = g_io_channel_win32_new_socket (s);
#else
  channel = g_io_channel_unix_new (s);
#endif
  g_assert (g_io_channel_set_encoding (channel, NULL, NULL) == G_IO_STATUS_NORMAL);
  g_io_channel_set_buffered (channel, FALSE);

  loop = g_main_loop_new (NULL, FALSE);
  g_assert (loop);

  thread = g_thread_create ((GThreadFunc) g_main_loop_run,
                             loop,
                             TRUE,
                             NULL);
  g_assert (thread);

 
  g_mutex_lock (mutex);

  g_print ("\n==> Adding a IN|ERR|OUT watch\n");
  source_id = g_io_add_watch (channel,
                               G_IO_IN | G_IO_ERR | G_IO_OUT,
                               send_cb,
                               (gpointer) FALSE);
  g_print ("==> source %d\n", source_id);
  g_cond_wait (cond, mutex);
  g_mutex_unlock (mutex);

  g_usleep (500000);

  g_print ("\n==> Removing source %d\n", source_id);
  g_source_remove (source_id);

  g_usleep (500000);

  g_mutex_lock (mutex);

  g_print ("\n==> Adding a IN|ERR|OUT watch\n");
  source_id = g_io_add_watch (channel,
                               G_IO_IN | G_IO_ERR | G_IO_OUT,
                               send_cb,
                               (gpointer) TRUE);
  g_print ("==> source %d\n", source_id);
  g_cond_wait (cond, mutex);
  g_mutex_unlock (mutex);

  g_main_loop_quit (loop);
  g_thread_join (thread);

  g_usleep (500000);

  g_print ("\n==> Removing source %d\n", source_id);
  g_source_remove (source_id);

  g_io_channel_unref (channel);

  return (0);
}
