/* Compile with:
 * gcc `pkg-config --cflags --libs gtk+-2.0 gthread-2.0` -o gtktalk ./gtktalk.c
 * */

/* VERSION 1.0.1 6/24/2003
   (c) 2003 Michael Plump  */

#include <gtk/gtk.h>
#include <glib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>

#define HOSTIP "208.151.247.42"
#define PORT 41000

void enter_callback(GtkWidget *widget, gpointer data);
gint disconnect(GtkWidget *widget, gpointer data);
void destroy(GtkWidget *widget, gpointer data);
void setup_the_connection(void);
void setup_the_threads(void);
void setup_the_widgets(void);
void input_thread(void *socket);

GtkTextBuffer *buffer;
GtkWidget *scroller;
GtkWidget *view;
int sock;
int setcbk = 0;

int main(int argc, char *argv[]) {

  g_thread_init(NULL);
  gdk_threads_init();
  gtk_init(&argc, &argv);

  setup_the_connection();

  setup_the_widgets();

  setup_the_threads();
  
  gtk_main();

  return 0;
}

void enter_callback(GtkWidget *widget, gpointer data) {
  int written;
  const char *text = gtk_entry_get_text(GTK_ENTRY(widget));
  written = write(sock, text, strlen(text));
  written = write(sock, "\n", 1);
  gtk_entry_set_text(GTK_ENTRY(widget), "");
}

gint disconnect(GtkWidget *widget, gpointer data) {
  close(sock);
  gtk_signal_emit_by_name(GTK_OBJECT(widget), "destroy");
  return TRUE;
}

void destroy(GtkWidget *widget, gpointer data) {
  gtk_main_quit();
}

void setup_the_connection(void) {
  struct sockaddr_in skaddr;
  char* username;

  if((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
    perror("Error creating socket");
    exit(1);
  }

  skaddr.sin_family = AF_INET;
  skaddr.sin_port = ntohs(PORT);
  skaddr.sin_addr.s_addr = inet_addr(HOSTIP);

  if(connect(sock, (struct sockaddr *) &skaddr, sizeof(skaddr)) < 0) {
    perror("Error connecting");
    exit(1);
  }
  username = getenv("USER");
  write(sock, "u=", 2);
  write(sock, username, strlen(username));
  write(sock, "\n", 1);
  write(sock, "h=gtktalk\n", 10);
  write(sock, "/signon\n", 8);
}

void setup_the_widgets(void) {
  GtkWidget *window;
  GtkWidget *entry;
  GtkWidget *vbox;
  PangoFontDescription *font_desc;

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(disconnect),
                   NULL);
  g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy),
                   NULL);
  gtk_window_set_default_size(GTK_WINDOW(window), 750, 500);

  view = gtk_text_view_new();
  gtk_text_view_set_editable(GTK_TEXT_VIEW(view), 0);
  font_desc = pango_font_description_from_string ("Monospace 12");
  gtk_widget_modify_font (view, font_desc);
  pango_font_description_free(font_desc);
  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
  gtk_widget_show(view);

  scroller = gtk_scrolled_window_new(NULL, NULL);
  gtk_container_add(GTK_CONTAINER(scroller), view);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
  gtk_widget_show(scroller);

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));

  entry = gtk_entry_new();
  g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(enter_callback),
                   NULL);
  gtk_widget_show(entry);

  vbox = gtk_vbox_new(0, 0);
  gtk_box_pack_start(GTK_BOX(vbox), scroller, 1, 1, 0);
  gtk_box_pack_start(GTK_BOX(vbox), entry, 0, 0, 0);
  gtk_widget_show(vbox);

  gtk_container_add(GTK_CONTAINER(window), vbox);
  gtk_widget_grab_focus(entry);
  gtk_widget_show(window);
}

void setup_the_threads(void) {
  GThread *input;
  input = g_thread_create((GThreadFunc) input_thread, NULL, 0, 0);
}

void input_thread(void *socket) {
  int bytesin;
  char buf[65536];
  GtkTextIter iter;

  while((bytesin = read(sock, buf, 65536)) > 0) {
    gdk_threads_enter();
    gtk_text_buffer_get_end_iter(buffer, &iter);
    gtk_text_buffer_place_cursor(buffer, &iter);
    gtk_text_buffer_insert(buffer, &iter, buf, bytesin);
    gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(view),
        gtk_text_buffer_get_insert(buffer), 0, TRUE, 0, 1);
    gdk_threads_leave();
  }
}
