/*
 * This file is part of the FEddi package
 *
 * Personal use allowed under the terms of the
 *
 *              GNU GENERAL PUBLIC LICENSE Version 2
 *              (see LICENSE for the complete text)
 *
 *-------------------------------------------------------------------
 *
 *    ENTER AT YOUR OWN RISK !!
 *
 * This source is without any documentation and can drive you mad.
 * In case of sudden epileptic seizures please call your doctor.
 *
 */

#define _MBUTIL_C

#include <stdio.h>
#include <stdlib.h>
#include <ncurses.h>
#include <string.h>
#include "proc.h"
#include "functs.h"
#include "colors.h"
#include "ctype.h"
#include "mbhelp.h"
#include "mbutil.h"
#include "inout.h"
#include "mbmenu.h"

extern LineListType *MsgTxt, *Kludges;
extern int MsgLines, KludgeLines;
extern int curMsg, curFld, maxFld;
extern FolderType *Folder;
extern MsgHdrType *MsgHdr;
extern LOCKType FElock;
extern WINDOW *wTxt,*wHdr,*wHlp;

extern void loadtxt(int);
extern void showmsg();
extern void findundel();
extern void reinit();
extern void redrawScreen();
extern int rescanfld();
extern void renumberfld();

int areaask(char *s, char *t);

int numUtility=0;
UtilityType *Utility=NULL;

void initutils()
{
	FILE *f;
	char file[PATH_MAX], *d1;
	int mode=0, meta, i, keys[26], j;
	sprintf(file,"%sutility",BasePath);
	if ((f=fopen(file,"rt"))!=NULL)
	{
		while (fgets(file,PATH_MAX,f))
		{
			if ((d1=strchr(file,'\n'))) *d1=0;
			if ((d1=strchr(file,'\r'))) *d1=0;
			for (d1=file; isspace(*d1); d1++);
			if (*d1==0 || *d1==';') continue;
			memmove(file, d1, strlen(d1)+1);
			if (!mode)
			{
				if (strncasecmp(file,"newutility ",11)==0)
				{
					if ((d1=strchr(file,32)))
					{
						d1++;
						if (d1[1]=='-')
						{
							if (*d1=='M')
								meta=1;
							else
								if (*d1=='C')
									meta=2;
								else
								{
									Log("!utility: Unknown key");
									meta=-1;
								}
						} else
							meta=0;
						if (meta!=-1 && isalpha(d1[meta?2:0]))
						{
							Utility=(UtilityType *)realloc(Utility,
									(numUtility+1)*UtilitySize);
							memset(&(Utility[numUtility]),0,UtilitySize);
							switch (meta)
							{
								case 0:
									Utility[numUtility].Key=d1[0];
									Utility[numUtility].MetaKey=0;
									break;
								case 1:
									Utility[numUtility].Key=d1[2];
									Utility[numUtility].MetaKey=1;
									break;
								case 2:
									Utility[numUtility].Key=toupper(d1[2])-'A'+1;
									Utility[numUtility].MetaKey=0;
									break;
							}
							mode=1;
						} else
							mode=-1;
					}
				} else
					mode=0;
			} else
			{
				if (mode==-1)
				{
					if (strcasecmp(file,"endutility")==0)
						mode=0;
					continue;
				}
				switch (mode)
				{
					case 1: /* Name */
						if (strlen(file)>20)
						{
							file[20]=0;
							Log("!Utility name too long");
						}
						Utility[numUtility].Name=strdup(file);
						mode++;
						break;
					case 2: /* Description */
						Utility[numUtility].Description=strdup(file);
						mode++;
						break;
					case 3: /* CommandLine */
						Utility[numUtility].CommandLine=strdup(file);
						mode++;
						break;
					default: /* Needs* Returns* endutility */
						if (strcasecmp(file,"endutility")==0)
						{
							mode=0;
							numUtility++;
							break;
						}
						if (strcasecmp(file,"needsheader")==0)
						{
							Utility[numUtility].NeedsHeader=1;
							break;
						}
						if (strcasecmp(file,"needstext")==0)
						{
							Utility[numUtility].NeedsText=1;
							break;
						}
						if (strcasecmp(file,"needskludges")==0)
						{
							Utility[numUtility].NeedsKludges=1;
							break;
						}
						if (strcasecmp(file,"needsscreen")==0)
						{
							Utility[numUtility].NeedsScreen=1;
							break;
						}
						if (strcasecmp(file,"needsmsgbase")==0)
						{
							Utility[numUtility].NeedsMsgBase=1;
							break;
						}
						if (strcasecmp(file,"needsarea")==0)
						{
							Utility[numUtility].NeedsArea=1;
							break;
						}
						if (strcasecmp(file,"takesmany")==0)
						{
							Utility[numUtility].TakesMany=1;
							break;
						}
						if (strncasecmp(file,"returns",7)==0 ||
								strcasecmp(file,"changesareas")==0)
						{
							Log("!%s no longer exists",file);
							break;
						}
						free(Utility[numUtility].Name);
						free(Utility[numUtility].Description);
						free(Utility[numUtility].CommandLine);
						mode=0;
						printERR("Errors in utility!\n");
						Log("!Errors in utility");
						break;
				}
			}
		}
		fclose(f);
		if (numUtility) {
			for (i=0; i<numUtility && i<10; i++)
			{
				if (strlen(Utility[i].Name)>Menu[UTILITY_MENU_PLACE].width)
						Menu[UTILITY_MENU_PLACE].width=strlen(Utility[i].Name);
			}
			sprintf(file,"%%-%us %%6s%%c",Menu[UTILITY_MENU_PLACE].width);
			Menu[UTILITY_MENU_PLACE].width+=8;
			memset(keys,0,4*26);
			for (i=0; i<numUtility && i<10; i++)
			{
				Menu[UTILITY_MENU_PLACE].entries++;
				sprintf(Menu[UTILITY_MENU_PLACE].entry[i],file,
						Utility[i].Name,
						Utility[i].MetaKey?"M-u M-":(Utility[i].Key<27?"M-u C-":"M-u "),
						Utility[i].Key+(Utility[i].Key<27?64:0));
				j=Menu[UTILITY_MENU_PLACE].width-1;
				if (isalpha(Menu[UTILITY_MENU_PLACE].entry[i][j]) &&
						!keys[tolower(Menu[UTILITY_MENU_PLACE].entry[i][j])-'a'])
				{
					Menu[UTILITY_MENU_PLACE].pos[i]=j;
					keys[tolower(Menu[UTILITY_MENU_PLACE].entry[i][j])-'a']=1;
				} else
					for (j=0; j<Menu[UTILITY_MENU_PLACE].width; j++)
					{
						if (isalpha(Menu[UTILITY_MENU_PLACE].entry[i][j]) &&
								!keys[tolower(Menu[UTILITY_MENU_PLACE].entry[i][j])-'a'])
						{
							Menu[UTILITY_MENU_PLACE].pos[i]=j;
							keys[tolower(Menu[UTILITY_MENU_PLACE].entry[i][j])-'a']=1;
							break;
						}
					}
			}
			maxMen++;
		}
	}
}

int utilitykey(int in, int meta)
{
	int i;
	if (!meta)
	{
		if (isalpha(in) || (in>0 && in<27))
		{
			for (i=0; i<numUtility; i++)
				if (!Utility[i].MetaKey && Utility[i].Key==in) break;
			if (i!=numUtility)
				return i;
		}
	} else
	{
		if (isalpha(in))
		{
			for (i=0; i<numUtility; i++)
				if (Utility[i].MetaKey && Utility[i].Key==in) break;
			if (i!=numUtility)
				return i;
		}
	}
	return -1;
}

int chooseUtility()
{
	int i, j, numut=0;
	char line[4096], name[21];
	LineListType *utils=NULL;
	if (!numUtility)
	{
		Beep();
		return -1;
	}
	for (i=0; i<numUtility; i++)
	{
		strcpy(name,Utility[i].Name);
		name[20]=0;
		for (j=strlen(name); j<20; j++) name[j]='.';
		strcpy(line,Utility[i].Description);
		line[COLS-30]=0;
		numut++;
		utils=(LineListType *)realloc(utils,numut*LineListSize);
		utils[numut-1].line=(char *)malloc(COLS);
		sprintf(utils[numut-1].line,"%s%c - %20s.%s",
				Utility[i].MetaKey?"M-":(Utility[i].Key<27?"C-":"  "),
				Utility[i].Key+(Utility[i].Key<27?'A'-1:0),
				name,line);
	}
	j=scrollchoice(utils,numut,"Call Utility",utilitykey);
	touchwin(wTxt);
	wrefresh(wTxt);
	for (i=0; i<numut; i++) free(utils[i].line);
	free(utils);
	return j;
}

int execUtility(int util)
{
	FILE *rp, *wp;
	char **args, *d1, *d2, areas[PATH_MAX]="", name[PATH_MAX], path[PATH_MAX];
	int lines=0, i, savemsg=0, pid, j;
	LineListType *status=NULL;
	WINDOW *lck=NULL;
	strcpy(name,Utility[util].CommandLine);
	if ((d1=strchr(name,32))) *d1++=0;
	if (!findfile(name,path))
	{
		Beep();
		return 0;
	}
	if (Utility[util].NeedsArea)
		if (areaask(areas,Utility[util].Name))
		{
			Beep();
			return 0;
		}
	savelogpos();
	if (Utility[util].NeedsMsgBase)
	{
		savemsghdr(&MsgHdr);
		MsgData.LastRead=curMsg;
		closearea();
		UNLOCKBase();
	}
	if (Utility[util].NeedsScreen)
		endwin();
	else
		EXTWait(Utility[util].Name);

	if (!(d2=strchr(name,'/')))
		d2=name;
	else
		d2++;
	args=(char **)malloc(2*sizeof(char *));
	args[0]=strdup(d2);
	for (i=1; d1; d1=d2)
	{
		while (*d1==32) d1++;
		if ((d2=strchr(d1,32))) *d2++=0;
		i++;
		args=(char **)realloc(args,(i+1)*sizeof(char *));
		args[i-1]=strdup(d1);
	}
	if (areas[0])
		for (d1=areas; d1; d1=d2)
		{
			while (*d1!='"' && *d1) d1++;
			if ((d2=strchr(d1+1,'"'))) *d2++=0;
			if (*d1)
			{
				i++;
				args=(char **)realloc(args,(i+1)*sizeof(char *));
				args[i-1]=strdup(d1+1);
			}
		}
	args[i]=NULL;
	if ((pid=pipecall(name,args,&rp,&wp))>0)
	{
		strcpy(name,Folder[curFld].name);
		pipeMsg(pid,rp,wp,curMsg,COLS,&savemsg,name,
				Utility[util].NeedsHeader?&(MsgHdr[curMsg]):NULL,
				&MsgLines,Utility[util].NeedsText?&MsgTxt:NULL,
				&KludgeLines,Utility[util].NeedsKludges?&Kludges:NULL,
				&lines,&status);
	}
	for (j=0; j<i; j++) free(args[j]);
	free(args);

	if (Utility[util].NeedsScreen)
	{
		reinit();
		redrawScreen();
	} else
	{
		EXTEnd();
		touchwin(wTxt);
		wrefresh(wTxt);
	}
	if (Utility[util].NeedsMsgBase)
	{
		while (LOCKBase(0))
		{
			if (lck)
			{
				mvwaddstr(lck,2,15,"Can't relock MsgBase");
				mvwaddstr(lck,4,10,"MsgBase is already locked by:");
				mvwprintw(lck,6,2,"%s (PID %u from %s)",FElock.Prog,FElock.PID,
						FElock.Host);
				mvwaddstr(lck,9,35,"<CR> to retry");
				wrefresh(lck);
				Beep();
				while (getchn()!=0xa);
			} else
			{
				if ((lck=openask(10,50,LINES/2-5,COLS/2-25,"LOCK ERROR",
						COL_WarningBox)))
				{
					mvwaddstr(lck,2,15,"Can't relock MsgBase");
					mvwaddstr(lck,4,10,"MsgBase is already locked by:");
					mvwprintw(lck,6,2,"%s (PID %u from %s)",FElock.Prog,FElock.PID,
							FElock.Host);
					mvwprintw(lck,7,2,"since %s",FElock.Date);
					mvwaddstr(lck,9,35,"<CR> to retry");
					wrefresh(lck);
					Beep();
					while (getchn()!=0xa);
				} else
					printERR("\nCan't relock MsgBase!\n");
			}
		}
		if (lck) delwin(lck);
	}
	if (Utility[util].NeedsMsgBase)
	{
		openarea(Folder[curFld].name,Folder[curFld].path);
		loadmsghdr(&MsgHdr);
		loadalias(Folder[curFld].name);
		loadorig(Folder[curFld].name);
		curMsg=MsgData.LastRead;
		findundel();
		loadtxt(0);
	}
	rescanfld();
	renumberfld();
	if (logposchanged())
	{
		if ((lines=showlog(&status)))
			scrolltext(status,lines,"Utility-log",0);
		for (i=0; i<lines; i++) free(status[i].line);
		free(status);
		status=0;
		lines=0;
	}
	if (lines)
	{
		scrolltext(status,lines,"Utility - status",0);
		for (i=0; i<lines; i++) free(status[i].line);
		free(status);
		status=0;
		lines=0;
	}
	return savemsg;
}

int areaask(char *a, char *t)
{
	int ret=1, i;
	char dum[512];
	WINDOW *ask=NULL;
	if ((ask=openask(6,20,LINES/2,COLS/2-10,t,COL_Menu))!=NULL)
	{
		mvwaddstr(ask,1,2,"<M>arked folders");
		mvwaddstr(ask,2,2,"<T>his folder");
		mvwaddstr(ask,3,2,"<A>ll folders");
		mvwaddstr(ask,4,2,"<ESC>^2 to abort");
		wrefresh(ask);
		for (;;)
		{
			switch (getchn())
			{
				case 0x1b: getch(); break;
				case 'm':
				case 'M':
					a[0]=0;
					for (ret=0, i=0; i<maxFld; i++)
						if (Folder[i].marked)
						{
							if (Folder[i].num==-1) ret=0;
							if (!ret)
							{
								sprintf(dum,"\"%s\" ",Folder[i].name);
								strcat(a,dum);
							}
							if (Folder[i].num==-1) ret=1;
						}
					ret=a[0]?0:1;
					break;
				case 't':
				case 'T':
					sprintf(a,"\"%s\"",Folder[curFld].name);
					ret=0;
					break;
				case 'a':
				case 'A':
					strcpy(a,"");
					ret=0;
					break;
				default: continue;
			}
			break;
		}
		delwin(ask);
		touchwin(wTxt);
		wrefresh(wTxt);
	} else
		Beep();
	return ret;
}

void showtxt(int shift);
void showhdr();
int savemsg(MsgHdrType *mh, int, int);

void execMenuUtility()
{
	if (execUtility(curMenpos))
		savemsg(&MsgHdr[curMsg],0,0);
	showhdr();
	showtxt(0);
	initmenu();
}
