/*---[ sort-clist.c ]-------------------------------------------------
 * Copyright (C) 2000, 2001 Tomas Junnonen (majix@sci.fi)
 *
 * 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 2 of the License, or
 * (at your option) any later version.
 *
 * Functions to sort the various clists
 *--------------------------------------------------------------------*/

#include <config.h>
#include <gnome.h>
#include <ctype.h>
#include "sort-clist.h"

struct typeofsort {
	gint clist;
	gint column;
	gint direction;
};

gint
(*compare_rows_cb) (GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2);
void
(*sort_cb) (GtkCList *clist, gint column, gpointer data);

/* [compare_ports]
 * Compare ports taken from two cells
 * eventually this function would be asigned to the 'compare_rows_cb' function pointer
 */
static gint
compare_ports (GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
{
	gchar *text1 = NULL;
	gchar *text2 = NULL;

	gint num1;
	gint num2;

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

	text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
	text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;

	if (!text2)
	    	return (text1 != NULL);
	if (!text1)
		return -1;

	num1 = atoi(text1);
	num2 = atoi(text2);

	if (num1 == num2)
		return 0;
	else
		return ((num1 < num2)?-1:1);
}

/* [sort_ip]
 * compare each number before the dots (.) of two ip addresses
 * this function is called from 'compare_src'
 */
static gint
sort_ip (gchar *text1, gchar *text2, gint k, gint m)
{
	gchar temp[4];
	gint i, num1, num2;

	for (i = 0; text1[k] != '.' && text1[k] != '\0'; i++, k++){
		temp[i] = text1[k];
	}
	temp[i] = '\0';
	num1 = atoi(temp);

	for (i = 0; text2[m] != '.' && text2[m] != '\0'; i++, m++){
		temp[i] = text2[m];
	}
	temp[i] = '\0';
	num2 = atoi(temp);

	if (num1 == num2) {
		if (text1[k] != '\0')
			return (sort_ip(text1, text2, ++k, ++m));
		else
			return 0;
	} else
		return ((num1 < num2)?-1:1);
}

/* [compare_src]
 * Compare two ip addresses or hostnames taken from two cells
 * evntually this function would be asigned to the 'compare_rows_cb' function pointer
 */
static gint
compare_src (GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
{
	gchar *text1 = NULL;
	gchar *text2 = NULL;
	gint i;
	gint ischar1 = 0;
	gint ischar2 = 0;
	GtkCListRow *row1 = (GtkCListRow *) ptr1;
	GtkCListRow *row2 = (GtkCListRow *) ptr2;

	text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
	text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
	if (!text2)
		return (text1 != NULL);
	if (!text1)
		return -1;
	for (i = 0; text1[i] != '\0'; i++) {
		if (text1[i] != '.' && !isdigit(text1[i])) {
			ischar1 = 1;
			break;
		}
	}
	for (i = 0; text2[i] != '\0'; i++) {
		if (text2[i] != '.' && !isdigit(text2[i])) {
			ischar2 = 1;
			break;
		}
	}
	if (ischar1 || ischar2) {
		if (ischar1 && ischar2)
			return strcmp (text1, text2);
		else if (ischar1)
			return 1;
		else
			return -1;
	} else {
		return (sort_ip(text1, text2, 0, 0));
	}
}

/* [number_for_month]
 * returns a diferent number for each month, 1 for January, 2 for February and so on,
 * this function is called from 'compare_date'
 */
static gint
number_for_month (gchar *text)
{
	switch (*text)
	{
	case 'J':
		if (*(text+1) == 'a')
			return 1;
		else if (*(text+2) == 'n')
			return 6;
		else
			return 7;
		break;
	case 'F':
		return 2;
		break;
	case 'M':
		if (*(text+2) == 'r')
			return 3;
		else
			return 5;
		break;
	case 'A':
		if (*(text+1) == 'p')
			return 4;
		else
			return 8;
		break;
	case 'S':
		return 9;
		break;
	case 'O':
		return 10;
		break;
	case 'N':
		return 11;
		break;
	case 'D':
		return 12;
		break;
	default:
		return 0;
		break;
	}
}

#define LOOP_CONDITION text[position] != ' ' && text[position] != ':' && text[position] != '\0'
/* [loop_compare]
 * takes two chars from a string and return the int value
 * this function is called from 'recursive_compare'
 */
static gint
loop_compare (gint position, gchar *text)
{
	gchar temp[3];
	gint i;

	position += (text[position] == ' ')?1:0;
	for (i = 0; LOOP_CONDITION; position++, i++)
		temp[i] = text[position];
	temp[i] = '\0';

	return(atoi(temp));
}
#undef LOOP_CONDITION

/* [recursive_compare]
 * compares the date and time of two strings
 * this function is called from 'compare_date'
 */
static gint
recursive_compare (gchar *text1, gchar *text2, gint position)
{
	gint num1, num2;

	num1 = loop_compare(position, text1);
	num2 = loop_compare(position, text2);
	if (num1 != num2)
		return ((num1 < num2)?-1:1);
	else {
		if ((position += 3) == 16)
			return 0;
		else
			return (recursive_compare(text1, text2, position));
	}
}

/* [compare_date]
 * compare two date taken from two cells
 * this function would be asigned to the 'compare_rows_cb' function pointer
 */
static gint
compare_date (GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
{
	gchar *text1 = NULL;
	gchar *text2 = NULL;

	gint num1 = 0;
	gint num2 = 0;

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

	text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
	text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;

	if (!text2)
	    	return (text1 != NULL);
	if (!text1)
	    	return -1;

	num1 = number_for_month(text1);
	num2 = number_for_month(text2);

	if (num1 != num2)
		return ((num1 < num2)?-1:1);
	else
		return (recursive_compare (text1, text2, 4));
	return 0;
}

/* [ pack_title_and_arrow ]
 * packs the title and the arrow in the button of a clist's column
 * this function is called from the 'packs_titles_and_arrows_in_columns' function.
 */
static void
pack_title_and_arrow ( GtkCList *clist, gint column)
{
	GtkWidget *hbox;
	GtkWidget *label;
	GtkWidget *arrow;

	hbox  = gtk_hbox_new (FALSE, 2);
	label = gtk_label_new (gtk_clist_get_column_title (clist, column));
	arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);

	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
	gtk_box_pack_end (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
	gtk_clist_set_column_widget (clist, column, hbox);

	gtk_widget_show (label);
	gtk_widget_show (hbox);
}

/* [ setup_arrow ]
 * hide, show and change the direction of an arrow in a clist column
 * this function is called from the 'packs_titles_and_arrows_in_columns',
 * 'sort_hitclist_cb' and 'sort_dynclist_cb' functions.
 */
static void
setup_arrow (GtkCList *clist, gint column, gint show, gint direction)
{
	GtkWidget *box;
	GtkBoxChild *child;

	box = gtk_clist_get_column_widget (clist, column);
	if (box == NULL)
		return;
	child = (GtkBoxChild *)GTK_BOX(box)->children->next->data;

	if (!show)
		gtk_widget_hide (GTK_WIDGET (child->widget));
	else {
		if (!GTK_WIDGET_VISIBLE (GTK_WIDGET (child->widget))){
			gtk_widget_show (GTK_WIDGET (child->widget));
		}
		if (direction == 1)
			gtk_arrow_set (GTK_ARROW (child->widget), GTK_ARROW_DOWN, GTK_SHADOW_OUT);
		else
			gtk_arrow_set (GTK_ARROW (child->widget), GTK_ARROW_UP, GTK_SHADOW_OUT);
	}
}

/* [ pack_titles_and_arrows_in_columns }
 * packs the titles and arrows in each column of the five clists,
 * this function is called from the 'prepare_for_sorting' function.
 */
static void
pack_titles_and_arrows_in_columns(GtkCList *clist, struct typeofsort *sort)
{
	int i;

	if (!sort->clist) {
		for (i = 0; i < 4; i++) {
			pack_title_and_arrow (clist, i);
		}
		setup_arrow (clist, 3, 1, 1);
	}else {
		pack_title_and_arrow (clist, 0);
		setup_arrow (clist, 0, 1, 1);
	}
}

/* [sort_hitclist_cb]
 * sorts the hitlogclist clist for port, source address, service or time
 * this function would be asigned to the 'sort_cb' function pointer
 */
static void
sort_hitclist_cb (GtkCList *clist, gint column, gpointer data)
{
	int width;
	struct typeofsort *temp;

	temp = (struct typeofsort *)data;
	gtk_clist_set_auto_sort (GTK_CLIST(clist), FALSE);

	if (column == 0)
		compare_rows_cb = compare_ports;
	else if (column == 1)
		compare_rows_cb = compare_src;
	else if (column == 3)
		compare_rows_cb = compare_date;
	else
		compare_rows_cb = NULL;

	gtk_clist_set_compare_func (GTK_CLIST(clist), compare_rows_cb);
	gtk_clist_set_sort_column (GTK_CLIST(clist), column);

	if (column == temp->column) {
		if ( temp->direction == 1) {
			temp->direction = 2;
			gtk_clist_set_sort_type (GTK_CLIST(clist), GTK_SORT_DESCENDING);
			setup_arrow (GTK_CLIST (clist), column, 1, 2);
		} else {
			temp->direction = 1;
			gtk_clist_set_sort_type (GTK_CLIST(clist), GTK_SORT_ASCENDING);
			setup_arrow (GTK_CLIST (clist), column, 1, 1);
		}
	} else {
		setup_arrow (GTK_CLIST (clist), column, 1, 1);
		setup_arrow (GTK_CLIST (clist), temp->column, 0, 0);
		temp->direction = 1;
		temp->column = column;
		gtk_clist_set_sort_type (GTK_CLIST(clist), GTK_SORT_ASCENDING);
		if (!column) {
			width = 6;
			width += gtk_clist_optimal_column_width (GTK_CLIST (clist), column);
			gtk_clist_set_column_width (GTK_CLIST (clist), column, width);
		}else if (column == 2){
			width = 12;
			width += gtk_clist_optimal_column_width (GTK_CLIST (clist), column);
			gtk_clist_set_column_width (GTK_CLIST (clist), column, width);
		}
	}

	gtk_clist_sort (GTK_CLIST (clist));
	gtk_clist_set_auto_sort (GTK_CLIST (clist), TRUE);
}

/* [free_structure_cb]
 * free the five typeofsort structures reserved for each clist when gtk_main_quit is called
 * this function is called from the 'prepare_for_sorting' function
 */
static gint
free_structure_cb (gpointer data)
{
	g_free ((struct typeofsort *)data);
	return 0;
}


/* [sort_dynclist_cb]
 * sorts any of the dynamic clists
 * this function would be asigned to the 'sort_cb' function pointer
 */
static void
sort_dynclist_cb (GtkCList *clist, gint column, gpointer data)
{
/*	struct typeofsort *temp;

	temp = (struct typeofsort *)data;
	gtk_clist_set_auto_sort (GTK_CLIST(clist), FALSE);
	compare_rows_cb = (temp->clist <= 2)?compare_src:NULL;

	if (temp->direction == 1) {
		temp->direction = 2;
		gtk_clist_set_sort_type (GTK_CLIST(clist), GTK_SORT_DESCENDING);
		setup_arrow (GTK_CLIST (clist), column, 1, temp->direction);
	} else {
		temp->direction = 1;
		gtk_clist_set_sort_type (GTK_CLIST(clist), GTK_SORT_ASCENDING);
		setup_arrow (GTK_CLIST (clist), column, 1, temp->direction);
	}

	gtk_clist_sort (GTK_CLIST(clist));
	gtk_clist_set_auto_sort (GTK_CLIST(clist), TRUE);
*/
}

/* [prepare_for_sorting]
 * this call all the others functions
 * is called one time for each clist in firestarter.c
 */
void
prepare_for_sorting (GtkWidget *clist, gint clistid)
{
	struct typeofsort *sort;

	if ( (sort = g_malloc(sizeof(struct typeofsort *))) == NULL ){
		g_print (_("Could not allocate memory, exiting...\n"));
		gtk_main_quit();
	}

	sort->column = (clistid != 0)?0:3;
	sort->direction = 1;
	sort->clist = clistid;

	pack_titles_and_arrows_in_columns(GTK_CLIST (clist), sort);

	if (clistid) {
		if (clistid <= 2)
			compare_rows_cb = compare_src;
		else
			compare_rows_cb = NULL;
		sort_cb = sort_dynclist_cb;
	} else {
		sort_cb = sort_hitclist_cb;
		compare_rows_cb = compare_date;
	}

	gtk_clist_set_compare_func (GTK_CLIST(clist), compare_rows_cb);
	gtk_clist_set_sort_column  (GTK_CLIST(clist), sort->column);
	gtk_clist_set_sort_type    (GTK_CLIST(clist), GTK_SORT_ASCENDING);
	gtk_clist_set_auto_sort    (GTK_CLIST(clist), TRUE);

	gtk_signal_connect (GTK_OBJECT(clist), "click-column",
		GTK_SIGNAL_FUNC (sort_cb), (gpointer)sort);

	gtk_quit_add (0, ((GtkFunction)free_structure_cb), (gpointer)sort);
}
