#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Dialog.h>
#include "RadioBtn.h"
#include "LinkText.h"
#include "PcBar.h"
#include "popup.h"

#include <stdio.h>
#include <stdlib.h>

#include "moviedb.h"
#include "dbutils.h"
#include "titlelist.h"
#include "cname.h"
#include "xregal.h"
#include "filmbuf.h"
#include "utility.h"
#include "history.h"
#include "LinkView.h"
#include "printmovie.h"
#include "searchHistory.h"

#define DIALOG_WIDTH 400

int cname_substring_stage=0;
Boolean cs_popped_up=False;
static char *searchname;
static int searchlist;
Widget cs_shell,
		cs_form,
		cs_box,
		cs_search,
		cs_cancel,
		cse_label,
		cse_text,
	 	cst_label,
		cs_type;

void printNameAndCharacter(char *name, char *cname) { 
	char  tmp[MXLINELEN];
	char  fmtline[MXLINELEN];
	char  *comma, *bracket;
	char hangindent[16];

	sprintf(hangindent, "<h %d>", app_data.hangingIndent);
	strcpy(tmp, name);
	if ((comma=strchr(tmp, ',')) != NULL) {
		if (*(comma + strlen(comma) - 1) == ')' &&
			(bracket = strstr(comma, " (")) != NULL) {
			*comma = '\0';
			*bracket = '\0';
			sprintf(fmtline, "%s %s<e> %s", comma + 2, tmp, bracket+1);
			}
		else {
			*comma = '\0' ;
			sprintf ( fmtline, "%s %s<e>", comma + 2, tmp );
			}
		}
	else
		sprintf(fmtline, "%s<e>", name);

	/* print out name/character name */
    sprintf(tmp, "(<f2><l -1:%s>%s as %s)<w0><i0><f1>\n", 
		name, fmtline, cname);

	AddToFilmbuf2(hangindent, tmp);
	}


/* Displays a list of movies in which a character name appears  */

void displayCharacterNameResults(TitleRec *thead, char *searchtext,
		int final_message) {
    TitleRec *trec ;
/*	struct akaRec *aka;*/
	char buf[128];
	int count=0;

	/*addAka(tchain);*/

	ClearFilmbuf();
  	trec=thead; 
	while (trec!=NULL) {
		count++;
		sprintf(buf, "%d.<i 20>", count);
		AddToFilmbuf(buf);
		AddToFilmbuf2("<w1><l MOV:", trec->title);
		AddToFilmbuf3(">", trec->title, "<e>  ");
		printNameAndCharacter(trec->aname, trec->cname);

		if (trec!=NULL)
			trec = trec->next;
		}

	switch (final_message) {
		case SEARCH_OVERFLOW:
			sprintf(buf, "\n<f2>Maximum of %d movies reached.\n", app_data.substringMax);
			AddToFilmbuf(buf);
			break;
		case SEARCH_ABORTED:
			AddToFilmbuf("\n<f2>Search aborted.\n");
			break;
		default:
			/* do nothing */
			;
		}

	AddToFilmbuf3("\n<m1><f2>", COPYRIGHT, "<f1><m0>");

	historySavePosition();
	SetTitle(searchtext);
	XtVaSetValues(viewer, XtNhypertext, Filmbuf(), NULL);
	LinkViewReload(vport, 0, 0);
	XtVaSetValues(url_text, XtNstring, "", NULL);
	historyAdd(Filmbuf(), searchtext, NULL);
	}

char* get_cname(FILE *dbFp, TitleID *titleKey, NameID *nameKey) {
	static int with, without, noWithAttr, noWithoutAttr;
	int len;
	char *cp=NULL;

	while (TRUE) {
		if ((with==0) && (without==0)) {
			*nameKey = getName(dbFp);
			if (feof(dbFp))
				return NULL;
			getFilmographyCounts(dbFp, &noWithAttr, &noWithoutAttr);
			}

		while (with < noWithAttr)  {
			*titleKey=getTitle(dbFp);
			with++;
		    getAttr(dbFp);
    		len = getByte(dbFp);
	    	if (len>0)
				cp=getString(len, dbFp);
		    getPosition(dbFp);
			if (cp!=NULL)
				return cp;
			}

		while (without  < noWithoutAttr) {
			*titleKey=getTitle(dbFp);
			without++;
			len = getByte(dbFp);
    	   	if ( len > 0 ) 
        	 	cp=getString ( len, dbFp ) ;
			getPosition ( dbFp ) ;
			if (cp!=NULL)
				return cp;
			}

		with=without=0;
		}
	}


Boolean doCharacterNameSearch(XtPointer client_data) {
	static TitleRec *head;
	static TitleRec *tail;
	static int lastpcdone=0;
	static FILE  *keyFp, *indexFp, *nameKeyFp, *nameIndexFp;
	int pcdone;
	static FILE *dbFp;
	char buf[MAXPATHLEN];
	char *cp, *mtitle, *aname;
	static int count;
	static long upper;
	TitleID  titleKey;
	NameID nameKey;
	int final_message;

	if (cname_substring_stage==0) {
		head=tail=NULL;
		count=0;
		if (searchlist==0)
			constructFilename(buf, ACRSTEM, DBSEXT );
		else
			constructFilename(buf, ACSSTEM, DBSEXT );
		dbFp = openFile(buf);
		fseek(dbFp, 0, SEEK_END);
		upper=ftell(dbFp);
		rewind(dbFp);
		indexFp = openFile ( TITLEIDX ) ;
		keyFp = openFile ( TITLEKEY ) ;
        nameKeyFp = openFile ( NAMEKEY ) ;
        nameIndexFp = openFile ( NAMEIDX ) ;
		cname_substring_stage++;
		return False;
		}

	if (cname_substring_stage==1) {
		while ((cp=get_cname(dbFp, &titleKey, &nameKey)) != NULL) {

			/* update display */
			pcdone=ftell(dbFp)*100/upper;
			if (pcdone != lastpcdone) {
				lastpcdone=pcdone;
				if (app_data.showprogress) {
					PcBarSetPercentage(pcbar, pcdone);
					sprintf(buf, "Matches on '%s': %d", searchname, count);
					SetTitle(buf);
					}
				if (app_data.showprogress) return False;
				}

			if (caseStrStr(cp, searchname) != NULL) {
				mtitle= mapTitleKeyToText(titleKey, keyFp, indexFp);
				aname= mapNameKeyToText(nameKey, nameKeyFp, nameIndexFp);
				if (count == 0) {
					head = newTitleRec();
					head->title = mtitle;
					head->aname = aname;
					head->cname = duplicateString(cp);
					head->titleKey = titleKey;
					tail=head;
					}
				else {
					tail->next = newTitleRec();
					tail = tail->next;
					tail->title = mtitle;
					tail->aname = aname;
					tail->cname = duplicateString(cp);
					tail->titleKey = titleKey;
					}
				count++;
				}
			free(cp);
			if (count>=app_data.substringMax)
				break;
			}

		fclose(dbFp);
		fclose ( indexFp ) ;
		fclose(keyFp);
		fclose(nameKeyFp);
		fclose(nameIndexFp);
		cname_substring_stage++;
		if (app_data.showprogress) return False;
		}

	PcBarSetPercentage(pcbar, 0);

	if (count==0) {
		Alert("No matches found!");
		XBell(XtDisplay(topLevel), 100);
	    SetBusy(viewer, False);
		cname_substring_stage=0;
		search_in_progress=False;
		return True;
		}

	if (cs_popped_up) {
		XtPopdown(cs_shell);
		cs_popped_up=False;
		enableSearch();
		}

	if (count>=app_data.substringMax)
		final_message=SEARCH_OVERFLOW;
	else if (cname_substring_stage==-1)
		final_message=SEARCH_ABORTED;
	else
		final_message=SEARCH_OK;
	displayCharacterNameResults(head, searchname, final_message);
    SetBusy(viewer, False);
	cname_substring_stage=0;
	search_in_progress=False;
	freeTitles(head);
	return True;
	}

static void Cancel(Widget w, XtPointer client_data, XtPointer call_data) {	
	enableSearch();
	XtPopdown(cs_shell);
	cs_popped_up=False;

	/* Cancel any substring search in progress */
	if (cname_substring_stage>0)
		cname_substring_stage=-1;
	search_in_progress=0;  /* redundant? reset in work procedure? */
    XDefineCursor(XtDisplay(topLevel), XtWindow(cs_form), unBusyCursor);
	}


static void Search(Widget w, XtPointer client_data, XtPointer call_data) {
	XtVaGetValues(cs_type, XtNselected, &searchlist, NULL);
	XtVaGetValues(cse_text, XtNstring, &searchname, NULL);
	AddStringToHistory(cse_text, searchname);

	/* guard against dumb substring searches */
 	trim_text(searchname, searchname);
	if (strlen(searchname)<2) {
		Alert("Search term must be at least 2 characters long!");
		XBell(XtDisplay(topLevel), 100);
		return;
		}

    XDefineCursor(XtDisplay(topLevel), XtWindow(cs_form), busyCursor);

	search_in_progress=True;
	XtAppAddWorkProc(app_context, doCharacterNameSearch, NULL);
	}

void CreateCharacterNameSearchDialog(void) {
    static String myreturn = "#override <Key>Return:   set() notify() unset()\n";
    static String myescape = "#override <Key>Escape:   set() notify() unset()\n";
    XtAccelerators accel, escaccel;

    accel = XtParseAcceleratorTable(myreturn);
    escaccel = XtParseAcceleratorTable(myescape);

	cs_shell=XtVaCreatePopupShell("cs_shell", transientShellWidgetClass,
                topLevel,
                XtNallowShellResize, False,
                NULL);

	cs_form=XtVaCreateManagedWidget("cs_form",formWidgetClass, cs_shell, NULL);
	cs_box=XtVaCreateManagedWidget("cs_box", boxWidgetClass, cs_form, 
		XtNwidth, DIALOG_WIDTH,
		XtNborderWidth, 0,
		NULL);
	cs_search=XtVaCreateManagedWidget("cs_search",commandWidgetClass, cs_box,
		XtNlabel, "Do Search",
        XtNaccelerators, accel,
		NULL);
	cs_cancel=XtVaCreateManagedWidget("cs_cancel",commandWidgetClass,cs_box,
		XtNlabel, "Cancel",
        XtNaccelerators, escaccel,
		NULL);

	cse_label=XtVaCreateManagedWidget("cse_label",labelWidgetClass, cs_form,
		XtNfromVert, cs_box,
		XtNwidth, 120,
		XtNborderWidth, 0,
		XtNjustify, XtJustifyLeft,
		NULL);
	cse_text = XtVaCreateManagedWidget("cse_text",asciiTextWidgetClass, cs_form,
        XtNeditType, XawtextEdit,
        XtNwidth, DIALOG_WIDTH-120,
        XtNresizable, True,
        XtNfromHoriz, cse_label,
		XtNfromVert, cs_box,
        XtNstring, searchname,
        XtNuseStringInPlace, False,
        NULL);
	AddHistoryText(cse_text);

	cst_label=XtVaCreateManagedWidget("cst_label",labelWidgetClass, cs_form,
		XtNfromVert, cse_label,
		XtNlabel, "Search:",
		XtNvertDistance, 15,
		XtNborderWidth, 0,
		XtNwidth, 120,
		XtNjustify, XtJustifyLeft,
		NULL);
	cs_type=XtVaCreateManagedWidget("cs_type",radioBtnWidgetClass, cs_form,
		XtNfromHoriz, cst_label,
		XtNfromVert, cse_label,
		XtNvertDistance, 15,
		XtNbuttons, "Actors list|Actresses list",
		XtNwidth, 120,
		NULL);

    XtAddCallback(cs_cancel, XtNcallback, Cancel, 0);
    XtAddCallback(cs_search, XtNcallback, Search, 0);
    XtSetKeyboardFocus(cs_form, cse_text);
    XtInstallAccelerators(cse_text, cs_search);
    XtInstallAccelerators(cse_text, cs_cancel);
	}


void PopupSearchCharacterName(void) {	
	disableSearch();
	XtVaSetValues(cse_text, XtNstring, NULL, NULL);
	SetPopupPosition(cs_shell, topLevel);
	XtPopup(cs_shell, XtGrabNone);
    XDefineCursor(XtDisplay(topLevel), XtWindow(cs_form), unBusyCursor);
	cs_popped_up=True;
	ResetHistory(cse_text);
	}
