/***************************************************************************
*																									*
*    MUISpell																					*
*    Spellchecking without texteditor, requires AlphaSpell and MUI			*
*    Copyright (C) 1996  Dirk Holtwick													*
*																									*
*    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.												*
*																									*
*    This program is distributed in the hope that it will be useful,			*
*    but WITHOUT ANY WARRANTY; without even the implied warranty of			*
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the			*
*    GNU General Public License for more details.									*
*																									*
*    You should have received a copy of the GNU General Public License		*
*    along with this program; if not, write to the Free Software				*
*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.					*
*																									*
*	  Author:   Dirk Holtwick																*
*					Karlstr. 59																	*
*					47119 Duisburg																*
*					GERMANY																		*
*					dirco@uni-duisburg.de													*
*																									*
****************************************************************************/

#define WLEN 256
#define TEXT(a) ((char*)&(a->str))
#define SUCC(a) a=a->succ;

BOOL	istext=FALSE,
		learned=FALSE,
		changed=FALSE;

struct StrData {
	struct  StrData *succ;
	char	  str;
} 	*ebase=0, *elast=0,
	*lbase=0, *llast=0;

BOOL	ramspell=FALSE,
		ramdict=FALSE;
char	*text,				// Der Originaltext
		*textact,			// Aktuelle Position im Text
		*textend,			// Ende des Textes
		*textmax,			// Maximale Ausdehnung des Textes
		*wordstart,			// Beginn des gefundenen Wortes
		*view,				// Der ViewPuffer
		textname[80],		// Name des Textes
		command[400],		// Befehlsstring fuer Execute()
		wort[WLEN],			// Einzelnes Wort (Hilfsvariable)
		pathspell[10],		// Pfad zu AlphaSpell
		pathdict[256];		// Pfad zu Wörterbüchern


// ZURECHTGESCHNITTENE Execute() FUNKTION
void Ex(char *s){
	if(!Execute(s,0,0) /*|| IoErr()*/) MUI_RequestA (App->App,App->win_view,0,NULL,"*_Okay",GetMUISpellString(m_req_aerr),0);
}

// COPY
void RAM(){
	ULONG	r;
	long	l;
	char	*path, *dict, buf[256], *b,*u;

	get (App->cy_ram, MUIA_Cycle_Active, &r);
	get (App->str_drawer, MUIA_String_Contents, &path);
	get (App->str_books, MUIA_String_Contents, &dict);
	get (App->str_user, MUIA_String_Contents, &u);

	if((r==1)||(r==3)){
		strcpy(pathspell,"ram:");
		if(!ramspell){
			Ex("c:copy c:alphaspell ram: >nil:");
			ramspell=TRUE;
		}
	}else strcpy(pathspell,"c:");
	if((r==1)||(r==2)){
		strcpy(pathdict,"ram:");
		if(!ramdict){
			while(TRUE){
				while(*dict==' ') dict++;
				b=buf;
				while(*dict && (*dict!=' ')) *b++=*dict++;
				*b=0;
				if(buf[0]){ 
					sprintf(command,"c:copy \"%s%s\" to ram: >nil:",path,&buf);
					Ex(command);
					if(!strcmp(buf+strlen(buf)-4,".mix")){
						buf[strlen(buf)-4]=0;
						strcat(buf,".mdx");
						sprintf(command,"c:copy \"%s%s\" to ram: >nil:",path,&buf);
						Ex(command);
					}else if(!strcmp(buf+strlen(buf)-4,".low")){
						buf[strlen(buf)-4]=0;
						strcat(buf,".ldx");
						sprintf(command,"c:copy \"%s%s\" to ram: >nil:",path,&buf);
						Ex(command);
					}
				}else break;
			}
			ramdict=TRUE;
			sprintf(command,"ram:%s.low",u);
			if(l=Lock(command,ACCESS_READ)){
				UnLock(l);
			}else{
				sprintf(command,"c:copy \"%s%s.mix\" \"%s%s.low\" \"%s%s.ldx\" \"%s%s.mdx\" to ram:",path,u,path,u,path,u,path,u);
				Ex(command);
			}
		}
	}else strcpy(pathdict,path);
}

BOOL chok (){
	if(changed){
		return(!MUI_RequestA (App->App,App->win_view,0,NULL,
			GetMUISpellString(m_reqbt_notsaved),
			GetMUISpellString(m_req_notsaved),0
		));
	}
	return(1);
}

// WORT ODER ZEILE DARSTELLEN
BOOL isword(){
	long	l;

	get (App->cy_line, MUIA_Cycle_Active, &l);
	return(!l);
}

// ERRORLISTE LOESCHEN
void freeerror(){
	struct StrData *act, *nact;

	act=ebase;
	while(act){
		nact=act->succ;
		free(act);
		act=nact;
	}
	ebase=elast=0;
}

// VORSCHLAGLISTE LOESCHEN
void freelist(){
	struct StrData *act, *nact;

	act=lbase;
	while(act){
		nact=act->succ;
		free(act);
		act=nact;
	}
	lbase=llast=0;
}

// SCHLAFEN
void sleep(BOOL b){
	set (App->App, MUIA_Application_Sleep, b);
}

// EIN WORT AUS DER FEHLERLISTE EINLESEN
char *getword (FILE *f){
	char	*w, c;

	w=wort;
	while(!feof(f) && ((c=fgetc(f))!='\n')) *w++=c;
	*w=0;
	if(feof(f)) return(0);
	return(wort);
}

// AUSGEWAEHLTEN STRING AKZEPTIEREN
void do_chstr(){
	long	l;
	char	*s;

	get (App->lv_spell, MUIA_List_Active, &l);
	DoMethod (App->lv_spell, MUIM_List_GetEntry, l, &s);
	set (App->str_spell, MUIA_String_Contents, s);
}

// BEREITE VIEWFENSTER AUF
void showpart(){
	ULONG	a,b;
	char	*t1,*t2;

	// Viewpuffer schaffen
	get (App->sl_forward, MUIA_Slider_Level, &b);
	get (App->sl_back, MUIA_Slider_Level, &a);

	a++; b++;

	t1=wordstart;
	while((t1>=text) && (a>0)){
		if(*t1=='\n') --a;
		t1--;
	}
	t1++;
	if(*t1=='\n') t1++;

	t2=textact;
	while((*t2) && (b>0)){
		if(*t2=='\n') --b;
		t2++;
	}

	if(view=malloc(t2-t1+100)){

		// Inhalt aufbauen
		strncpy(view, t1, wordstart-t1);
		strcat (view,"\0338\033b");
		strcat (view,wort);
		strcat (view,"\0330\033n");
		strncat (view, textact, t2-textact);

		set (App->lv_view, MUIA_Floattext_Text, view);
		set (App->txt_view, MUIA_Text_Contents, wort);
		set (App->gauge_txt, MUIA_Gauge_Current, wordstart-text);


		// Listerinhalt löschen
		DoMethod (App->lv_spell, MUIM_List_Clear, 0);
		freelist();

		set (App->str_spell, MUIA_String_Contents, wort);

		free(view);
		view=0;
	}else  MUI_RequestA (App->App,App->win_view,0,NULL,"*_Okay",GetMUISpellString(m_req_nomem),0);
	set (App->win_view, MUIA_Window_ActiveObject, App->str_spell);
}

// ZEILENENDE und ANFANG suchen
void findline(){
	char	*s,*w;

	do{
		wordstart--;
	}while((text<=wordstart) && (*wordstart!='\n'));
	wordstart++;
	while((*textact) && (*textact!='\n')) textact++;

	w=wort;
//	textact--;
	s=wordstart;
	while(s<textact) *w++=*s++;
	*w=0;

	if(*textact) istext=TRUE;
	else istext=FALSE;
	showpart();
}

// WORT AN LOESUNGSLISTE ANHAENGEN
void addlist (char *s){
	struct StrData *act;

	if(act=malloc(strlen(s)+sizeof(struct StrData))){
		if(lbase) llast->succ=act;
		else lbase=act;
		llast=act;
		act->succ=0;
		strcpy(TEXT(act),s);

		// Wort in die Listview-Liste einfuegen
		DoMethod(App->lv_spell, MUIM_List_InsertSingle, TEXT(act), MUIV_List_Insert_Bottom);

	}else MUI_RequestA (App->App,App->win_view,0,NULL,"*_Okay",GetMUISpellString(m_req_nomem),0);
}

// WOERTER IM TEXT SUCHEN
char *findword(){
	char	*w;

	while(*textact && !isalpha(*textact)) textact++;
	w=wort;
	wordstart=textact;
	while(*textact && isalpha(*textact)) *w++=*textact++;
	*w=0;
	if(wort[0]) return(wort);
	else return(0);	
}

// WOERTER MIT FEHLERN VERGLEICHEN
char *finderrword(){
	struct	StrData	*act;

	while(findword()){
		act=ebase;
		while(act){
			if(!strcmp(TEXT(act),wort)) return(wort);
			SUCC(act);
		}
	}
	return(0);
}


// WOERTER IM TEXT SUCHEN
char *findbackword(){
	char	*w;

	textact=wordstart;
	do{
		textact--;
	}while((text<textact) && !isalpha(*textact));
	if(text<textact){
		do{
			textact--;
		}while((text<wordstart) && isalpha(*textact));

		w=wort;
		wordstart=++textact;
		while(*textact && isalpha(*textact)) *w++=*textact++;
		*w=0;
		if(wort[0]) return(wort);
	}else{
		textact=wordstart=text;
		wort[0]=0;
	}
	return(0);
}

// WOERTER MIT FEHLERN VERGLEICHEN
char *findbackerrword(){
	struct	StrData	*act;

	while(findbackword()){
		act=ebase;
		while(act){
			if(!strcmp(TEXT(act),wort)) return(wort);
			SUCC(act);
		}
	}
	return(0);
}

// RUECKWAERTS SPRINGEN
void do_back(){

	DoMethod (App->pa_spell, MUIM_Popstring_Close);
	sleep(TRUE);
	if(isword()){
		istext=(BOOL)findbackerrword();
		showpart();
	}else{
		textact=--wordstart;
		if(textact<text) textact=wordstart=text;
		findline();
	}
	sleep(FALSE);
}

// FEHLERHAFTE WOERTER SUCHEN
void do_goon(){

	DoMethod (App->pa_spell, MUIM_Popstring_Close);
	sleep (TRUE);
	if(isword()){
		istext=(BOOL)finderrword();
		showpart();
	}else{
		if(*textact!=0){
			wordstart=++textact;
			findline();
		}
	}
	sleep(FALSE);
}


// WORT AN ERRORLISTE ANHAENGEN
void adderror (char *s){
	struct StrData *act;

	if(act=malloc(strlen(s)+sizeof(struct StrData))){
		if(ebase) elast->succ=act;
		else ebase=act;
		elast=act;
		act->succ=0;
		strcpy(TEXT(act),s);
	}else MUI_RequestA (App->App,App->win_view,0,NULL,"*_Okay",GetMUISpellString(m_req_nomem),0);
}


// SPEICHER WIEDER FREIGEBEN
void freeall(){
	freeerror();
	freelist();
	if(view) free(view);
}


// DATEIGROESSE
long lgetsize(char *fname){
	long		size=0,locksave;
	struct 	FileInfoBlock  *fileinfo;

	// FileInfoBlock einlesen
	fileinfo=malloc(sizeof(struct FileInfoBlock));
	if (locksave=Lock(fname,ACCESS_READ)){
		if(Examine(locksave,fileinfo)) size=fileinfo->fib_Size;
		UnLock(locksave);
	}else return(0);
	free(fileinfo);
	return(size);
}


// TEXT LADEN UND ANALYSIEREN
void do_load(){
	FILE		*f;
	ULONG		size, percent;
	char		*b;

	if(chok()){

	if (MUI_AslRequest (fr, 0)){

		strcpy (textname, fr->fr_Drawer);
		AddPart(textname, fr->fr_File, 80);

		// Besser schlafen
		sleep(TRUE);
	
		// Dateigroesse
		if(size=lgetsize(textname)){
		
			// Fehler suchen
			set (App->gauge_txt,	 MUIA_Gauge_Max, 		  size);
//			get (App->str_drawer, MUIA_String_Contents, &d);
			get (App->str_books,  MUIA_String_Contents, &b);
//			get (App->str_user,   MUIA_String_Contents, &u);

			RAM();

			sprintf (command,"%salphaspell -Ss \"%s\" -o t:text.cor -d \"%s\" %s",pathspell,textname,pathdict,b);
			Ex (command);
		
			// Fehlerliste einlesen
			freeerror();
			if(f=fopen("t:text.cor","r")){
				while(getword(f)) adderror(wort);
				istext=TRUE;
				changed=FALSE;
				fclose(f);
			}else  MUI_RequestA (App->App,App->win_view,0,NULL,"*_Okay",GetMUISpellString(m_req_fileerr),0);
		
			// TEXT EINLESEN
		
			// Puffer schaffen (+ Korrekturspielraum)
			get (App->sl_buffer, MUIA_Slider_Level, &percent);
			if(text=malloc(size+((percent*size)/100))){
				textend=text+size;
				textmax=textend+((percent*size)/100);

				// Laden
				if (f=fopen(textname,"rb")){
					fread(text,size,1,f);
					fclose(f);
				}else MUI_RequestA (App->App,App->win_view,0,NULL,"*_Okay",GetMUISpellString(m_req_fileerr),0);
		
				*textend=0;
				textact=text;
			}else MUI_RequestA (App->App,App->win_view,0,NULL,"*_Okay",GetMUISpellString(m_req_nomem),0);
		
			// Suche ersten Fehler
			do_goon();

		}else  MUI_RequestA (App->App,App->win_view,0,NULL,"*_Okay",GetMUISpellString(m_req_fileerr),0);
		
		// Jetzt kann's weiter gehen
		sleep(FALSE);
	}
	}
}

void do_save(){
	FILE *f;

	sleep(TRUE);
	if(f=fopen(textname,"wb")){
		fwrite(text, textend-text, 1, f);
		changed=FALSE;
		fclose(f);
	}
	sleep(FALSE);
}

void do_saveas(){
	if (MUI_AslRequest (fr, 0)){
		strcpy (textname, fr->fr_Drawer);
		AddPart(textname, fr->fr_File, 80);
		do_save();
	}
}

void do_guess(){
	char	*w,*b,oldwort[WLEN];
	long	r;
	FILE	*f;

	sleep(TRUE);
	strcpy(oldwort,wort);

	// Lister mit Loesungen fuellen
	DoMethod (App->lv_spell, MUIM_List_Clear, 0);
	freelist();
	get (App->str_spell, MUIA_String_Contents, &w);

	// Vorschlaege sammeln
//	get (App->str_drawer, MUIA_String_Contents, &d);
	get (App->str_books,  MUIA_String_Contents, &b);
//	get (App->str_user,   MUIA_String_Contents, &u);
	get (App->sl_rate,    MUIA_Slider_Level, &r);

	RAM();

	sprintf (command,"%salphaspell >nil: -Gw \"%s\" -n %ld -o t:text.sug -d \"%s\" %s",pathspell,w,r,pathdict,b);
	Ex(command);

	// Vorschläge lesen
	if(f=fopen("t:text.sug","r")){
		while(getword(f)) addlist(wort);
		fclose(f);
	}else MUI_RequestA (App->App,App->win_view,0,NULL,"*_Okay",GetMUISpellString(m_req_fileerr),0);

	strcpy(wort,oldwort);
	sleep(FALSE);

	DoMethod (App->pa_spell, MUIM_Popstring_Open);
}

ULONG do_popup(register __a1 Object *win){
	set (win, MUIA_Window_DefaultObject, App->lv_spell);;
	set (App->lv_spell, MUIA_List_Active, 0);
	return(TRUE);
}

void do_accept(){
	char	*s,*ws;
	BOOL  doit=TRUE;
	int	l=0;

	DoMethod (App->pa_spell, MUIM_Popstring_Close);
	sleep(TRUE);

	// Wort aendern
	get (App->str_spell, MUIA_String_Contents, &s);

	if(istext){
		// Platz schaffen
		if(strlen(s)!=strlen(wort)){
			l=strlen(s)-strlen(wort);
			if(textmax<(textend+l)){
				MUI_RequestA (App->App,App->win_view,0,NULL,"*_Okay",GetMUISpellString(m_req_nospace),0);
				doit=FALSE;
			}else{
				memcpy(textact+l,textact, textend-textact+1);
				textact+=l;
				textend+=l;
			}
		}

		strcpy(wort,s);
		// Wort einsetzen
		ws=wordstart;
		if(doit) while(*s) *ws++=*s++;
		textact=ws;
		changed=TRUE;
		showpart();
	}

	// Naechstes Wort
//	do_goon();

	sleep(FALSE);
}

void do_lvaccept(){
	long	l;
	char	*s;

	get (App->lv_spell, MUIA_List_Active, &l);
	DoMethod (App->lv_spell, MUIM_List_GetEntry, l, &s);
	set (App->str_spell, MUIA_String_Contents, s);
	do_accept();
}

// USE
void do_use_prefs(){
	DoMethod(App->App,MUIM_Application_Save,MUIV_Application_Save_ENV);
	set(App->win_prefs, MUIA_Window_Open, FALSE);
}

// SAVE
void do_save_prefs(){
	DoMethod(App->App,MUIM_Application_Save,MUIV_Application_Save_ENV);
	DoMethod(App->App,MUIM_Application_Save,MUIV_Application_Save_ENVARC);
	set(App->win_prefs, MUIA_Window_Open, FALSE);
}

//CANCEL
void do_cancel_prefs(){
	DoMethod(App->App,MUIM_Application_Load,MUIV_Application_Load_ENV);
	set(App->win_prefs, MUIA_Window_Open, FALSE);
}

// IGNORIEREN
void do_ignore(){
	char	*w,r=TRUE;
	struct StrData *act,*lact;

	sleep(TRUE);
	get (App->str_spell, MUIA_String_Contents, &w);

	// Wort aus der Liste löschen
	act=ebase;
	while(act && r){
		if(!strcmp(TEXT(act),w)){
			if(act==ebase) ebase=act->succ;
			else lact->succ=act->succ;
			free(act);
			r=FALSE;
		}
		lact=act;
		SUCC(act);	
	}

   do_goon();

	sleep(FALSE);
}

// EDIT DISTANCE
void do_editdist(){
	char	*s;

	// Slider auf die angemessene Groesse bringen
	get (App->str_keyfile, MUIA_String_Contents, &s);
	if(*s) set (App->sl_rate, MUIA_Slider_Max, 20);
	else set (App->sl_rate, MUIA_Slider_Max, 2);
}

// PREFERNCES
void do_books(register __a1 struct FileRequester *fr){
	struct WBArg *ap;
	static char buf[256];
	int	i;

	strcpy (buf,fr->fr_Drawer);
	AddPart (buf, "", 255);
	set (App->str_drawer, MUIA_String_Contents, buf);
	buf[0]=0;
	for (ap=fr->fr_ArgList,i=0;i<fr->fr_NumArgs;i++,ap++){
		strcat(buf,ap->wa_Name);
		strcat(buf," ");
	}
	set (App->str_books, MUIA_String_Contents, &buf);
}

// PREFERNCES
void do_bookstart(register __a1 ULONG *tlist){
	char	*d;

	get (App->str_drawer, MUIA_String_Contents, &d);
	*tlist++ = ASLFR_InitialDrawer;
	*tlist++ = (ULONG)d;
	*tlist++ = 0;
	*tlist++ = 0;
}

void do_drawer(){
	char	*d, buf[256];

	get (App->str_drawer, MUIA_String_Contents, &d);
	strcpy (buf,d);
	AddPart (buf, "", 255);
	set (App->str_drawer, MUIA_String_Contents, &buf);
}

// WORT LERNEN
void do_learn(){
	char	*w,*ww,*u,*k,suf[4];
	ULONG	l;

	sleep(TRUE);

	get (App->str_spell,   MUIA_String_Contents, &w);
	get (App->str_user,    MUIA_String_Contents, &u);
//	get (App->str_drawer,  MUIA_String_Contents, &d);
	get (App->str_keyfile, MUIA_String_Contents, &k);

	ww=w;
	strcpy(suf,"low");
	while(*ww){
		if(isupper(*ww)) strcpy(suf,"mix");
		ww++;
	}

	RAM();

	sprintf(command,"%s%s.%s",pathdict,u,suf);
	if(l=Lock(command,ACCESS_READ)){
		if(*k) sprintf(command,"%salphaspell >nil: -cLw \"%s\" -k \"%s\" -o \"%s%s.%s\" \"%s%s.%s\"",pathspell,w,k,pathdict,u,suf,pathdict,u,suf);
		else sprintf(command,"%salphaspell >nil: -cLw \"%s\" -o \"%s%s.%s\" \"%s%s.%s\"",pathspell,w,pathdict,u,suf,pathdict,u,suf);
		UnLock(l);
	}else{
		if(*k) sprintf(command,"%salphaspell >nil: -cLw \"%s\" -k \"%s\" -o \"%s%s.%s\"",pathspell,w,k,pathdict,u,suf);
		else sprintf(command,"%salphaspell >nil: -cLw \"%s\" -o \"%s%s.%s\"",pathspell,w,pathdict,u,suf);
	}
	Ex(command);

	learned=TRUE;
	do_ignore();

	sleep(FALSE); 
}


// GANZE ZEILE DARSTELLEN
void do_line(){

	sleep(TRUE);
	if(isword()){
		textact=wordstart;
		do_goon();
	}else{
		findline();
	}
	sleep(FALSE);
}

