#include <stdlib.h>
#include "taskfunc.h"
#include "menu.h"
#include "mandata.h"
#include "context.h"
#include "util.h"
#include "gman.h"
#include <string.h>
#include "window2.h"
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>

//#include <gtk/gtk.h>

/********************* init_man_data *************************/
int init_man_data()
{
	ManPath * path;
	char * path_name,*p1,*p2;
	int end = 0;
	man_paths = new Dictionary;
	path_name = my_strdup((char*)context->get_value("man_paths"));
	p1 = path_name;
	while(!end) {
		p2 = strchr(p1,(int)':');
		if(p2 == NULL) end++;
		else *p2 = '\0';
		man_paths->add_item(my_strdup(p1),(void*)( new ManPath(p1)));
		p1 = p2+1;
	}
	if(path_name) free (path_name);
	return 0;
}

int extract_man_data()
{
	ManItem ** buffer;
	int i,j,count;
	int display_section_ID;

	pthread_mutex_lock(&context_lock);
	switch ((int)context->get_value("display_section_policy")) {
	case 0: display_section_ID = ~0;break;
	case 1: display_section_ID = ~(int)(context->get_value("display_section"));break;
	case 2: display_section_ID = (int)context->get_value("display_section"); break;
	default: fprintf(stderr,"warning: init_man_data: \"display_section_policy\" "
					 "have invalid value %d",context->get_value("display_section_policy"));
	display_section_ID = ~0;
	}
	pthread_mutex_unlock(&context_lock);

	if(man_items_count) free(man_items_buffer);
	j = man_paths->get_size();
	for(i = man_items_count = 0;i<j;i++) 
		man_items_count += ((ManPath*)(man_paths->get_value(i)))->GetSize(display_section_ID);
	buffer = (ManItem**)malloc(sizeof(ManItem*)*man_items_count);
	man_items_buffer = (char**) buffer;
	for(i = count = 0;i<j;i++)
		count += ((ManPath*)(man_paths->get_value(i)))->GetItems(display_section_ID,buffer+count);
	if( count != man_items_count) exit(1);

	if(context->get_value("debuging")) printf("count = %d\n",count);
	qsort((void *)buffer,count,sizeof(ManItem*),(int(*)(const void*,const void*))&man_item_compare);
	if(context->get_value("debuging")) printf("count = %d, qsort end.\n",count);

	return 0;
}
	
int add_data_to_clist(int flag)
{
	gchar *text[2];
	static int i;
	int j,k;
	ManItem ** buffer;
	static char ** pointer;
	char a[100],b[20];
	int display_section_ID;

	text[0] = a; text[1] = b;
	buffer = (ManItem**) man_items_buffer;

	pthread_mutex_lock(&gtk_lock);

	if(flag & TASK_START) {
		i = 0;
		pointer = man_items_buffer;
		gtk_clist_clear(GTK_CLIST(clist));
	}

	gtk_clist_freeze(GTK_CLIST(clist));
	for (j = 0;j<100 & i< man_items_count;i++,j++) {
		buffer[i]->get_display_name(a);
		buffer[i]->get_section_name(b);
		gtk_clist_append(GTK_CLIST(clist),text);
		gtk_clist_set_row_data(GTK_CLIST(clist),i,(gpointer)buffer[i]);
		gtk_clist_get_text(GTK_CLIST(clist),i,0,pointer++);
	}
	gtk_clist_thaw(GTK_CLIST(clist));
	pthread_mutex_unlock(&gtk_lock);

	//pointer = man_items_display_name;
	//for(int i = 0;i<man_items_count;i++) printf(*(pointer++));
	return (i < man_items_count);
}

int loading_man_data()
{
	ManPath * p;
	char * c;
	int i;
	char buffer[100];
	if(!man_paths_to_be_load->get_size()) return 0;
	pthread_mutex_lock(&loading_man_path_lock);
	c = (char*)man_paths_to_be_load->get_item(0);
	man_paths_to_be_load->delete_item(0);
	pthread_mutex_unlock(&loading_man_path_lock);
	p = new ManPath(c);
	i = man_paths->search_item(c);
	if (i == -1) {
		delete (p);
		g_warning("man path %s does not exist\n",c);
		return man_paths_to_be_load->get_size();
	}
	//	printf("loading... %d end\n",i);
	man_paths->set_value(i,(void*)p);
	if(clist2) {
		sprintf(buffer,"%d",p->GetSize(-1));
		pthread_mutex_lock(&gtk_lock);
		gtk_clist_set_text(GTK_CLIST(clist2),i,2,buffer);
		pthread_mutex_unlock(&gtk_lock);
	}
	return man_paths_to_be_load->get_size();
}

int is_blank(char c) {return (c==0x20||c==0x09)?1:0;}

typedef enum
{
  S_START,
  S_NAME,
  S_NAME_END1,
  S_NAME_END2,
  S_SECTION,
  S_SECTION_END1,
  S_SECTION_END2,
  S_MINUS,
  S_COMMENT_START,
  S_COMMENT
} STATES;

#define BUF_SIZE 400

static List * names;	//the names returned are strdup() , need to free out side
static char * comment;
static char section[BUF_SIZE/4];

static int parser_whatis(int fd) 
{
	static int init = 0;
	int i,j,k,end;
	static char buffer[BUF_SIZE];
	char a,b,c;

	if(!init) {
		names = new List;
		init ++;
	}

	names->delete_all();
	i = 0;
	end = 0;
	STATES s = S_START;
	while(!end) {
		k = read(fd,&c,1);
		//g_print("%c",c,k);
		if(k == -1) g_error("key word task:reading from pipe:%s",g_strerror(errno));
		if (k == 0 || c == 0x0a) {
			if (s == S_COMMENT) {
				buffer[i] = 0;
				// add items to clist
				//for(j = 0;j<names->get_size();j++) g_print("%s , ",(char*)names->get_item(j));
				//g_print("section = %s, \tcomment = %s\n",section,buffer);
				comment = buffer;
			}
			else if( s != S_START) {
				if(context->get_value("show_warning")) 
					g_warning("parser error: unexpected end of %s",k?"line":"file");
				comment = NULL;
			}
			end++;
			continue;
		} 
		switch (s) {
		case S_START:
			if(!is_blank(c)) {
				s = S_NAME;
				i = 0;
				buffer[i++] = c;
			}
			break;
		case S_NAME:
			if( c == ',') {
				s = S_NAME_END1;
				buffer[i] = 0;
				names->add_item(my_strdup(buffer));
			} else if(is_blank(c)) {
				s = S_NAME_END2;
			}else {
				buffer[i++] = c;
			}
			break;
		case S_NAME_END1:
			if(is_blank(c)) break;
			else if(c == '(') {
				s = S_SECTION;
				i = 0;
			}
			else {
				s = S_NAME;
				i = 0;
				buffer[i++] = c;
			}
			break;
		case S_NAME_END2:
			if(is_blank(c)) break;
			else if(c == '(') {
				buffer[i] = 0;
				names->add_item(my_strdup(buffer));
				s = S_SECTION;
				i = 0;
			} else if(c == ',') {
				s = S_NAME_END1;
				buffer[i] = 0;
				names->add_item(my_strdup(buffer));
			}
			else {
				s = S_NAME;
				buffer[i++] = c;
			}
			break;
		case S_SECTION:
			if(is_blank(c)) break;
			else if(c == ')') {
				s = S_SECTION_END1;
				if(i == 0) {
					g_warning("parser error: unknown format");
				}
				else {
					buffer[i] = 0;
					strcpy(section,buffer);
				}
			}
			else buffer[i++] = c;
			break;
		case S_SECTION_END1:
			if(is_blank(c)) break;
			else if(c == '-') s = S_SECTION_END2;
			break;
		case S_SECTION_END2:
			if(is_blank(c)) s = S_COMMENT_START;
			else g_warning("parser error: unknwn format");
			break;
		case S_COMMENT_START:
			if(!is_blank(c)) {
				s = S_COMMENT;
				i = 0;
				buffer[i++] = c;
			}
			break;
		case S_COMMENT:
			buffer[i++] = c;
			break;
		default: g_warning("parser error: unknow state");
		}
	}
	//g_print("k = %d\n",k);
	return k;
}

List * keyword_search_list;

static int key_word_search_taskfunc(int flag)
{
	static int init = 0;
	static char * parameter;
	static int process_ID;
	static int pipes[2];
	static List * paths;
	static char * path;
	static int pipe_active; // pipe is in active
	static int running; // pipe is in active
	static int counter;

	ManPath * man_path;
	ManItem * man_item;
	int i,j,k;
	char file_name[BUF_SIZE], buffer[BUF_SIZE];
	char * text[3];
	char * s;

	if(!init) {
		pipe_active = 0;
		running = 0;
		paths = new List();
		keyword_search_list = new List();
		init ++;
	}

	//g_print("task key-word search ...\n");
	if((flag & (TASK_START|TASK_STOP)) && pipe_active) {
		//g_print("reset ...\n");
		kill(process_ID,3);
		close(pipes[0]);
		pipe_active = 0;
		running = 0;
		gtk_widget_set_sensitive(stop_button,0);
		//paths->delete_all();
	}
	if(flag & TASK_START) {
		//g_print("starting ... key = %s\n",keyword);
		running = 1;
		gtk_widget_set_sensitive(stop_button,1);
		paths->delete_all();
		gtk_clist_clear(GTK_CLIST(clist3));
		counter = 0;
		j = man_paths->get_size();
		for(i = 0;i<j;i++)	paths->add_item(man_paths->get_name(i));
	} else if( !pipe_active ) {
		if(paths->get_size() == 0) {
			running = 0;
			gtk_widget_set_sensitive(stop_button,0);
		} else {
			path = (char*) paths->get_item(0);
			paths->delete_item(0);
			strcpy(buffer,"grep ");
			strcat(buffer,keyword);
			strcat(buffer," ");
			attach(file_name,path,"whatis");
			strcat(buffer,file_name);
			strcat(buffer," ");

			g_return_val_if_fail(!pipe(pipes),0);
			process_ID = fork();
			if(process_ID == -1) {g_warning("can not fork a new process");return 0;}
			if(process_ID == 0) {
				g_return_val_if_fail(close(pipes[0]) != -1,0);;
				close(1);
				g_return_val_if_fail(dup(pipes[1]) == 1,0);
				g_return_val_if_fail(close(pipes[1]) != -1,0);
				//write(1,"1234567890safdsfd%srfwfwefw",10);
				//fprintf(stderr,"%s\n",buffer);
				if(do_system_command(buffer,0));// g_warning("my_system returned a non-zero value!");
				_exit(0);
			}
			close(pipes[1]);
			pipe_active ++;
		}
	} else {	//running, pipe in active
		k = parser_whatis(pipes[0]);
		if((j = names->get_size())) {
			/*
			for(i = 0;i<j;i++) 
				g_print(i?", %s":"%s",names->get_item(i));
			g_print("(%s)",section);
			g_print(" - %s\n",comment);
			*/
			man_path = (ManPath*)man_paths->get_value(man_paths->search_item(path));
			text[1] = section;
			text[2] = "";
			for(i = 0;i<j;i++) {
				//g_print("point 0, man_item = %x\n",man_path);
				s = (char*)names->get_item(i);
				if(s[0] == '/') s = strrchr(s, '/') + 1;
				man_item = (ManItem*)man_path->search_man_item(s,section);
				//g_print("point 1\n");
				if(context->get_value("show_warning")) 
					if (!man_item) g_warning("man item: %s (%s) could not found",names->get_item(i),section);
				text[0] = (char*)names->get_item(i);
				//g_print("point 2\n");
				if(i == j-1) text[2] = comment;
				pthread_mutex_lock(&gtk_lock);
				gtk_clist_append(GTK_CLIST(clist3),text);
				gtk_clist_set_row_data(GTK_CLIST(clist3),counter++,(gpointer)man_item);
				pthread_mutex_unlock(&gtk_lock);
			}
		}
		if(k == 0) {
			close(pipes[0]);
			pipe_active = 0;
			//g_print("pipe closeing \n");
		} 
	}
	return running;
}
	
/******************* init thread **********************/
Task *task_init_man_data;
Task *task_extract_man_data;
Task *task_add_data_to_clist;
Task *task_loading_man_data;
Task *task_key_word_search;
void init_thread(TaskGroup * thread)
{
	task_init_man_data = task_new(thread,0.3,(TaskRunFunc)init_man_data,NULL);
	task_set_active(task_init_man_data);
	task_extract_man_data = task_new(thread,0.5,(TaskRunFunc)extract_man_data,NULL);
	task_set_active(task_extract_man_data);
	task_add_data_to_clist = task_new(thread,0.7,(TaskRunFunc)add_data_to_clist,NULL);
	task_set_active(task_add_data_to_clist);
	task_loading_man_data = task_new(thread,0.31,(TaskRunFunc)loading_man_data,NULL);
	task_key_word_search = task_new(thread,0.5,(TaskRunFunc)key_word_search_taskfunc,NULL);
}
