#include "addressbook.h"
#include <stdlib.h>
#include <string.h>
#include <gtk/gtkinvisible.h>


static GtkWidget *clist;
static addr_info info;
static gint num_entries;



/******** External function declarations ********/

void addressbook_clist_set_function(GtkWidget *clist);
void addressbook_get_grey_colors(GdkColor *darkgrey, GdkColor *lightgrey);
gint read_address_book(GtkWidget *clist);
void add_new_record(GtkWidget *widget, gpointer gdata);
void lookup_record(GtkWidget *widget, GdkEventKey *event);
void delete_record(gchar *key);
void edit_record(gint row, gchar *key);



/******** Internal function declarations ********/

static void delete(GtkWidget *widget, GdkEventKey *event);
static void edit(GtkWidget *widget, GdkEventKey *event);
guint list_size_allocate_handler_id;
static void list_size_allocate_handler(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data);
void move_to_address(gint row, gboolean make_top);
#if (LOOKUP_FILE == 1)
gint lookup_from_file(const gchar *s, gchar *key, gint start_index);
#else
gint lookup_from_list(gchar *s, gchar *key);
#endif
static void make_info_display(GtkWidget *tbox);
static void init_list_items();
static void sort_and_shade_list(gint sort);
static gint compare_func(GtkCList *list, gconstpointer a, gconstpointer b);
#if LINEAR_SEARCH
static gint list_linear_search(gchar *key, gchar *buf);
#endif



/******** External function definitions ********/

void remove_from_list(gint row)
{
   gtk_clist_remove(GTK_CLIST(clist), row);

   /* one row less in list now */
   num_entries--;

   make_info_display(NULL);

   /* do shading only */
   sort_and_shade_list(0);

   if((row = GTK_CLIST(clist)->focus_row) >= 0)
   {
      /* make sure focus row is highlighted after deletion*/
      move_to_address(row, FALSE);
   }
}



void update_address_list(gint row, addr_info *aip)
{
   gchar *s, key[100], buf[100], *strval[2];
   gint i;

   /* construct name column text */
   sprintf(buf, "%s, %s", aip->lname, aip->fname);

   if(row < 0)
   {
      /* a new entry is added so insert a row */
      strval[NAME_INX] = buf;
      strval[PHONE_INX] = aip->work;
      gtk_clist_append(GTK_CLIST(clist), strval);

      /* one row more in list */
      num_entries++;
      make_info_display(NULL);
   }
   else
   {
      /* an old entry is updated so update the row */
      gtk_clist_get_text(GTK_CLIST(clist), row, NAME_INX, &s);

      if(strcmp(s, buf) != 0)
      {
         /* key is changed, a new row must be inserted */
         update_address_list(-1, aip);
         return;
      }
      else
      {
         /* update the work phone no. column only */
         gtk_clist_set_text(GTK_CLIST(clist), row, PHONE_INX, aip->work);
      }
   }

   /* do both shading and sorting */
   sort_and_shade_list(1);

   /* scroll to the newly inserted row */
   if(row < 0)
   {
#if (LOOKUP_FILE == 1)
      if((i = lookup_from_file(strval[NAME_INX], key, 0)) >= 0)
#else
      if((i = lookup_from_list(strval[NAME_INX], key)) >= 0)
#endif
         move_to_address(i, FALSE);
   }
}



addr_info *get_current_record()
{
   return &info;
}


#if LOOKUP_FILE
gint lookup_from_list(gchar *s, gchar *buf)
{
#if LINEAR_SEARCH
   return list_linear_search(s, buf);
#else
   gchar *p;
   gint len, try, start, row, end, flg;

   /* Do a binary search in the name column only */
   start = 0;
   end = num_entries;
   len = strlen(s);
   try = 0;

   while(start != end)
   {
      if(try == 1)
         start++;
      else if(try > 1)
         break;

      row = (start + end) / 2;

      gtk_clist_get_text(GTK_CLIST(clist), row, NAME_INX, &p);

      flg = strncasecmp(p, s, len);

      if(flg > 0)
         end = row;
      else if(flg < 0)
         start = row;
      else
         return row;

      try += (end - start == 1);
   }

   return -1;
#endif
}
#endif


void make_window_header(GtkWidget *vbox)
{
   GtkWidget *tbox, *button;

   /* Create a topbox for showing information */
   tbox = gtk_hbox_new(FALSE, ALL_SPACE);
   gtk_widget_set_usize(GTK_WIDGET(tbox), TOPBOX_WD, MENU_HEIGHT);
   gtk_box_pack_start(GTK_BOX(vbox), tbox, FALSE, FALSE, FIRST_SPACE);
   gtk_widget_show(tbox);

   /* Create a text label for showing information */
   make_info_display(tbox);

   button = gtk_button_new_with_label("Delete");
   gtk_box_pack_end(GTK_BOX(tbox), button, FALSE, FALSE, 0);
   gtk_widget_show(button);
   gtk_signal_connect(GTK_OBJECT(button), "clicked",
                      GTK_SIGNAL_FUNC(delete), NULL);
   
   button = gtk_button_new_with_label("Find...");
   gtk_box_pack_end(GTK_BOX(tbox), button, FALSE, FALSE, 0);
   gtk_widget_show(button);
   gtk_signal_connect(GTK_OBJECT(button), "clicked",
                      GTK_SIGNAL_FUNC(lookup_record), NULL);
   
   button = gtk_button_new_with_label("Add");
   gtk_box_pack_end(GTK_BOX(tbox), button, FALSE, FALSE, 0);
   gtk_widget_show(button);
   gtk_signal_connect(GTK_OBJECT(button), "clicked",
                      GTK_SIGNAL_FUNC(add_new_record), NULL);
   
   button = gtk_button_new_with_label("Edit");
   gtk_box_pack_end(GTK_BOX(tbox), button, FALSE, FALSE, 0);
   gtk_widget_show(button);
   gtk_signal_connect(GTK_OBJECT(button), "clicked",
                      GTK_SIGNAL_FUNC(edit), NULL);
}



void make_window_list(GtkWidget *vbox)
{
   GtkWidget *sclwin;
   gint offset;

   offset = 2;

   sclwin = gtk_scrolled_window_new(NULL, NULL);
   gtk_container_border_width(GTK_CONTAINER(sclwin), -offset);
   list_size_allocate_handler_id = gtk_signal_connect(GTK_OBJECT(sclwin), "size-allocate",
                                                      GTK_SIGNAL_FUNC(list_size_allocate_handler), NULL);
   //gtk_box_pack_start(GTK_BOX(vbox), sclwin, FALSE, FALSE, SECOND_SPACE);
   gtk_box_pack_start(GTK_BOX(vbox), sclwin, TRUE, TRUE, 0); // This scrolled_window can be resized
   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (sclwin),
                        GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
   gtk_widget_show(sclwin);

   /* Create a clist with 2 columns */
   clist = gtk_clist_new(2);
   gtk_clist_set_compare_func(GTK_CLIST(clist), compare_func);
   gtk_container_border_width(GTK_CONTAINER(clist), 0);
   gtk_container_add(GTK_CONTAINER(sclwin), clist);
   gtk_widget_show(clist);

   /* Set column width */
   gtk_clist_set_column_width(GTK_CLIST(clist), NAME_INX, NAME_WD);
   gtk_clist_set_column_width(GTK_CLIST(clist), PHONE_INX, PHONE_WD);
   
   /* Set row height */
   gtk_clist_set_row_height(GTK_CLIST(clist), LIST_ROW_HT);

   /* Set text justification to the column */
   gtk_clist_set_column_justification(GTK_CLIST(clist), NAME_INX, GTK_JUSTIFY_LEFT);
   gtk_clist_set_column_justification(GTK_CLIST(clist), PHONE_INX, GTK_JUSTIFY_LEFT);

   addressbook_clist_set_function(clist);

   init_list_items();
}


/******** Internal function definitions ********/

static void delete(GtkWidget *widget, GdkEventKey *event)
{
   gchar *key;
   gint row = GTK_CLIST(clist)->focus_row;

   if(row > -1 && gtk_clist_get_text(GTK_CLIST(clist), row, NAME_INX, &key))
   {
   	  delete_record(key);
   	  remove_from_list(row);
   }
}


static void edit(GtkWidget *widget, GdkEventKey *event)
{
   gchar *key;
   gint row = GTK_CLIST(clist)->focus_row;

   if(row > -1 && gtk_clist_get_text(GTK_CLIST(clist), row, NAME_INX, &key))
   {
   	  edit_record(row, key);
   }
}


static void list_size_allocate_handler(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
{
	gtk_signal_disconnect(GTK_OBJECT(widget), list_size_allocate_handler_id);
	gtk_widget_set_usize(widget, allocation->width, WINDOW_HEIGHT - allocation->y);
}


static void make_info_display(GtkWidget *tbox)
{
   static GtkWidget *label;
   gchar buf[80];

   if(label == NULL)
   {
      /* Create a text label for showing information */
      label = gtk_label_new("");
      gtk_box_pack_start(GTK_BOX(tbox), label, FALSE, FALSE, 5);
      gtk_widget_show(label);
   }

   /* Update the text label for the information */
#if (PCFB || PDA)
   sprintf(buf, "Address Book (records: %d)", num_entries);
#else
   sprintf(buf, "Number of records = %d", num_entries);
#endif

   gtk_label_set_text(GTK_LABEL(label), buf);
}



static void init_list_items()
{
   num_entries = read_address_book(clist);

   /* do both shading and sorting */
   sort_and_shade_list(1);

   make_info_display(NULL);

   if (num_entries > 0)
	   move_to_address(0, TRUE);
}



static void sort_and_shade_list(gint sort)
{
   GdkColor lightgrey, darkgrey;
   gint i, j;

   if(num_entries == 0)
      return;

   if(sort)
   {
      /* sort name column in list */
      gtk_clist_set_sort_type(GTK_CLIST(clist), GTK_SORT_ASCENDING);
      gtk_clist_set_sort_column(GTK_CLIST(clist), NAME_INX);
      gtk_clist_sort(GTK_CLIST(clist));
   }

   /* set background colour for row */
   addressbook_get_grey_colors(&darkgrey, &lightgrey);

   /* shade one row for every three */
   for(i = j = 0; i < num_entries; i++, j++)
   {
      if(j == 2)
      {
         gtk_clist_set_background(GTK_CLIST(clist), i, &darkgrey);
         j = -1;
      }
      else
         gtk_clist_set_background(GTK_CLIST(clist), i, &lightgrey);
   }
}


static gint compare_func(GtkCList *list, gconstpointer ptr1, gconstpointer ptr2)
{
	char *text1 = NULL;
	char *text2 = NULL;

	GtkCListRow *row1 = (GtkCListRow *)ptr1;
	GtkCListRow *row2 = (GtkCListRow *)ptr2;

	text1 = GTK_CELL_TEXT (row1->cell[0])->text;
	text2 = GTK_CELL_TEXT (row2->cell[0])->text;
	
	if (!text2)
	  return (text1 != NULL);

	if (!text1)
	  return -1;

	return strcasecmp(text1, text2);
}


#if LINEAR_SEARCH
static gint list_linear_search(gchar *key, gchar *buf)
{
   gchar *p, ts[512], ks[512];
   gint i, f;

   /* Do a binary search in the name column only */
   for(i = 0; i < num_entries; i++)
   {
      gtk_clist_get_text(GTK_CLIST(clist), i, NAME_INX, &p);

      /* make copy of key and convert to upper case */
      g_strup(strcpy(ts, p));
      g_strup(strcpy(ks, key));

      f = (strstr(ts, ks) != NULL);

      if(f != 0)
      {
         strcpy(buf, p);

         /* return row index */
         return i;
      }
   }

   return -1;
}
#endif



