/*

Directory Opus 4
Original GPL release version 4.12
Copyright 1993-2000 Jonathan Potter

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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

All users of Directory Opus 4 (including versions distributed
under the GPL) are entitled to upgrade to the latest version of
Directory Opus version 5 at a reduced price. Please see
http://www.gpsoft.com.au for more information.

The release of Directory Opus 4 under the GPL in NO WAY affects
the existing commercial status of Directory Opus 5.

*/

#include "dopus.h"
#include "view.h"

start_external(func)
struct dopus_func_start *func;
{
	int arg;
	char path[256],*ptr;

	func->key=NULL;
	func->status=0;
	func->resseg=NULL;

	if (!(func->replyport=LCreatePort(NULL,0))) return(0);
	if (!(func->startup.wbstartup.sm_ArgList=LAllocRemember(&func->key,
		sizeof(struct WBArg)*(func->argcount+1),MEMF_CLEAR|MEMF_PUBLIC)))
		return(0);

	func->startup.wbstartup.sm_Message.mn_Node.ln_Type=NT_MESSAGE;
	func->startup.wbstartup.sm_Message.mn_ReplyPort=func->replyport;
	func->startup.wbstartup.sm_Message.mn_Length=(UWORD)sizeof(struct DOpusStartup);

	func->startup.wbstartup.sm_NumArgs=func->argcount;
	for (arg=0;arg<func->argcount;arg++)
		func->startup.wbstartup.sm_ArgList[arg].wa_Name=func->args[arg];

	strcpy(path,func->segname);
	if (ptr=BaseName(path)) *ptr=0;
	if (!(func->startup.wbstartup.sm_ArgList[0].wa_Lock=Lock(path,ACCESS_READ)))
		return(0);

	func->startup.retcode=0;

	func->status=FS_CURDIR;
	func->olddir=CurrentDir(func->startup.wbstartup.sm_ArgList[0].wa_Lock);

	if (!func->segment) {
		if (system_version2) {
			Forbid();
			if (func->resseg=FindSegment(func->segname,NULL,0)) {
				func->resseg->seg_UC++;
				func->segment=func->resseg->seg_Seg;
			}
			Permit();
		}
		if (!func->segment &&
			!(func->segment=(BPTR)LoadSeg(func->segname)))
			return(0);
	}

	func->status=FS_SEGMENT;

	func->startup.wbstartup.sm_Segment=func->segment;
	if (!(func->startup.wbstartup.sm_Process=CreateProc(func->procname,
		main_proc->pr_Task.tc_Node.ln_Pri,func->startup.wbstartup.sm_Segment,func->stack)))
		return(0);

	func->startup.wbstartup.sm_ToolWindow=NULL;
	PutMsg(func->startup.wbstartup.sm_Process,(struct Message *)&func->startup);

	func->status=FS_LAUNCHED;
	return(1);
}

close_external(func,wait)
struct dopus_func_start *func;
int wait;
{
	if (func->status>=FS_LAUNCHED && wait) {
		WaitPort(func->replyport);
		GetMsg(func->replyport);
	}
	if (func->status>=FS_SEGMENT) {
		Forbid();
		if (func->resseg) {
			func->resseg->seg_UC--;
			func->segment=0;
		}
		else if (!func->flags&FF_SAVESEG && func->segment) {
			UnLoadSeg(func->segment);
			func->segment=0;
		}
		Permit();
	}
	if (func->status>=FS_CURDIR) {
		CurrentDir(func->olddir);
		UnLock(func->startup.wbstartup.sm_ArgList[0].wa_Lock);
	}
	if (func->replyport) LDeletePort(func->replyport);
	LFreeRemember(&func->key);
	return(func->startup.retcode);
}

void doconfig()
{
	char buf[100],buf1[20],replyname[50],portname[50],funcpath[80];
	int old_bufcount,a;
	struct MsgPort *conport,*cmdport;
	struct dopusconfigmsg *repmsg;
	struct configconfig cfg,*rcfg;
	struct ConfigStuff cstuff;
	struct dopus_func_start config_func;
	char *func_args[2];
	char *old_name;
	char old_language[30];

	if (!(checkwindowquit())) return;

	lsprintf(replyname,"dopus4_config_reply%ld",system_dopus_runcount);
	if (!(conport=LCreatePort(replyname,20))) return;

	lsprintf(buf,"%ld",system_dopus_runcount);
	lsprintf(buf1,"dopus4_config%ld",system_dopus_runcount);

	strcpy(funcpath,"ConfigOpus");

	if (!configopus_segment) {
		dostatustext(globstring[STR_LOADING_CONFIG]);
		FindSystemFile("ConfigOpus",funcpath,80,SYSFILE_MODULE);
	}

	func_args[0]=funcpath;
	func_args[1]=buf;

	config_func.segment=configopus_segment;
	config_func.procname="ConfigOpus";

	config_func.segname=funcpath;
	config_func.argcount=2;
	config_func.args=func_args;
	config_func.stack=8000;
	config_func.flags=(config->loadexternal&LOAD_CONFIG)?FF_SAVESEG:0;

	if (!(start_external(&config_func))) {
		close_external(&config_func,0);
		dostatustext(globstring[STR_CONFIG_NOT_FOUND]);
		LDeletePort(conport);
		return;
	}

	dostatustext(globstring[STR_WAITING_FOR_PORT]);
	lsprintf(portname,"dopus4_config_port%ld",system_dopus_runcount);
	cmdport=NULL;

	for (a=0;a<100;a++) {
		Forbid();
		cmdport=FindPort(portname);
		Permit();
		if (cmdport) break;
		Delay(5);
	}

	if (!cmdport) {
		close_external(&config_func,0);
		dostatustext(globstring[STR_CONFIG_NOT_FOUND]);
		LDeletePort(conport);
		return;
	}

	status_configuring=-1;
	shutthingsdown(10);
	old_bufcount=config->bufcount;
	strcpy(old_language,config->language);
	dotaskmsg(hotkeymsg_port,HOTKEY_KILLHOTKEYS,0,0,NULL,0);

	endnotifies();

	Forbid();
	old_name=arexx_port->mp_Node.ln_Name;
	arexx_port->mp_Node.ln_Name="foo";
	Permit();

	FOREVER {
		while (repmsg=(struct dopusconfigmsg *)GetMsg(conport)) {
			switch (repmsg->command) {
				case CONFIG_ALL_DONE:
					ReplyMsg((struct Message *)repmsg);
					goto configdone;
				case CONFIG_GET_CONFIG:
					cfg.config=config;
					cfg.firsttype=dopus_firsttype;
					cfg.typekey=filetype_key;
					cfg.firstbank=dopus_firstgadbank;
					cfg.changed=config_changed;
					cfg.firsthotkey=dopus_firsthotkey;
					strcpy(cfg.configname,str_config_basename);
					repmsg->buffer=(char *)&cfg;
					break;
				case CONFIG_HERES_CONFIG:
					rcfg=(struct configconfig *)repmsg->buffer;
					dopus_firsttype=rcfg->firsttype;
					dopus_firstgadbank=rcfg->firstbank;
					dopus_firsthotkey=rcfg->firsthotkey;
					filetype_key=rcfg->typekey;
					config_changed=rcfg->changed;
					StrCombine(str_config_file,rcfg->configname,".CFG",256);
					strcpy(str_config_basename,rcfg->configname);
					Window=rcfg->Window; MainScreen=rcfg->Screen;
					status_configuring=1;
					break;
				case CONFIG_NEW_HOTKEY:
					dotaskmsg(hotkeymsg_port,HOTKEY_HOTKEYCHANGE,0,0,NULL,0);
					break;
			}
			ReplyMsg((struct Message *)repmsg);
		}

		Wait(1<<conport->mp_SigBit);
	}

configdone:
	config_func.flags=(config->loadexternal&LOAD_CONFIG)?FF_SAVESEG:0;
	close_external(&config_func,1);
	configopus_segment=config_func.segment;

	status_configuring=-1;
	Window=NULL; MainScreen=NULL;
	dopus_curgadbank=dopus_firstgadbank;
	data_gadgetrow_offset=data_drive_offset=0;
	dotaskmsg(hotkeymsg_port,HOTKEY_HOTKEYCHANGE,0,0,NULL,0);
	dotaskmsg(hotkeymsg_port,HOTKEY_NEWHOTKEYS,0,0,NULL,0);
	if (config->bufcount!=old_bufcount) {
		allocdirbuffers(config->bufcount);
		for (a=0;a<2;a++) strcpy(str_pathbuffer[a],dopus_curwin[a]->directory);
	}
	if (strcmp(old_language,config->language)) read_data_files(0);

	for (a=0;a<3;a++) {
		if (!(config->loadexternal&(1<<a)) && external_mod_segment[a]) {
			UnLoadSeg(external_mod_segment[a]);
			external_mod_segment[a]=0;
		}
	}

	fixcstuff(&cstuff);
	CheckConfig(&cstuff);
	SetUp(2);

	LDeletePort(conport);

	Forbid();
	arexx_port->mp_Node.ln_Name=old_name;
	Permit();

	rexx_command(config->configreturnscript,NULL);

	startnotifies();
	status_configuring=0;
}

static char *external_modules[3]={
	"DOpus_Disk",
	"DOpus_Print",
	"DOpus_Icon"};

struct DiskData {
	int function;
	char funcpath[80];
	char *args[16];
	int argcount;
	int background;
};

void dopus_diskop(function,rexx,bg)
int function,rexx,bg;
{
	struct ArbiterLaunch launch;
	struct DiskData *disk_data;
	struct DOpusRemember *memkey=NULL;
	int a,fail=1;

	if ((disk_data=LAllocRemember(&memkey,sizeof(struct DiskData),MEMF_CLEAR))) {
		dostatustext(globstring[STR_STARTING_DISK_MODULE]);

		disk_data->function=function;
		disk_data->background=bg;

		strcpy(disk_data->funcpath,external_modules[SEG_DISK]);
		if (external_mod_segment[SEG_DISK] ||
			(FindSystemFile(external_modules[SEG_DISK],disk_data->funcpath,80,SYSFILE_MODULE))) {
			disk_data->args[0]=disk_data->funcpath;

			switch (function) {
				case FUNC_FORMAT:
				case FUNC_BGFORMAT:
					disk_data->args[1]="f";
					break;
				case FUNC_DISKCOPY:
				case FUNC_BGDISKCOPY:
					disk_data->args[1]="d";
					break;
				case FUNC_INSTALL:
				case FUNC_BGINSTALL:
					disk_data->args[1]="i";
					break;
				default:
					return;
			}

			disk_data->argcount=3;

			if (rexx) {
				if ((disk_data->argcount+=rexx_argcount)>16) disk_data->argcount=16;
				for (a=3;a<disk_data->argcount;a++) {
					if (disk_data->args[a]=LAllocRemember(&memkey,strlen(rexx_args[a-3])+1,0))
						strcpy(disk_data->args[a],rexx_args[a-3]);
				}
			}

			for (a=disk_data->argcount;a<16;a++) disk_data->args[a]="";

			launch.launch_code=(void *)launch_diskop;
			launch.launch_name="dopus_disk_launch";
			launch.launch_memory=memkey;
			launch.data=(APTR)disk_data;

			if (!bg) data_window_refresh=0;

			arbiter_command(ARBITER_LAUNCH,(APTR)&launch,(bg)?0:ARB_WAIT);

			if (!bg) {
				for (a=0;a<2;a++) {
					if (data_window_refresh&(1<<a)) {
						strcpy(str_pathbuffer[a],dopus_curwin[a]->realdevice);
						startgetdir(a,1);
					}
				}
			}

			okay();
			fail=0;
		}
	}
	if (fail) dostatustext(globstring[STR_UNABLE_TO_LOAD_MODULE]);
}

void __saveds launch_diskop()
{
	struct Process *my_process;
	struct ArbiterMessage *my_startup_message;
	struct DiskData *disk_data;
	struct MsgPort *control_port;
	struct dopus_func_start disk_func;
	struct DOpusMessage *dopusmsg;
	struct Screen *screen;
	char portname[22],portbuf[22];
	int retcode=-1;

	my_process=(struct Process *)FindTask(NULL);
	my_process->pr_WindowPtr=(APTR)-1;
	WaitPort(&my_process->pr_MsgPort);
	my_startup_message=(struct ArbiterMessage *)GetMsg(&my_process->pr_MsgPort);

	disk_data=(struct DiskData *)(my_startup_message->data);

	if (control_port=CreateUniquePort("DOPUS_DISK",portbuf,NULL)) {
		disk_func.segment=external_mod_segment[SEG_DISK];
		disk_func.procname=external_modules[SEG_DISK];
		disk_func.segname=disk_data->funcpath;

		StrCombine(portname,"&",portbuf,20);
		disk_data->args[2]=portname;

		disk_func.argcount=disk_data->argcount;
		disk_func.args=disk_data->args;
		disk_func.stack=5000;
		disk_func.flags=(config->loadexternal&LOAD_DISK)?FF_SAVESEG:0;

		if (start_external(&disk_func)) {
			struct DOpusRemember *memory=NULL;
			int newscreen=0;

			screen=NULL;

			if (disk_data->background && MainScreen &&
				(screen=open_subprocess_screen(globstring[STR_DISK_OP_TITLE],
					scr_font[FONT_REQUEST],&memory,NULL))) newscreen=1;

			FOREVER {
				if (GetMsg(disk_func.replyport)) break;
				while (dopusmsg=(struct DOpusMessage *)GetMsg(control_port)) {
					switch (dopusmsg->command) {
						case DOPUSMSG_GETVIS:
							fill_out_visinfo((struct VisInfo *)dopusmsg->data,screen);
							break;
						case DOPUSMSG_UPDATEDRIVE:
							if (!disk_data->background) {
								BPTR lock,testlock;
								int win,ret;
								char *drive;

								drive=(char *)dopusmsg->data;
								if (drive && drive[0]) {
									if (lock=Lock(drive,ACCESS_READ)) {
										for (win=0;win<2;win++) {
											if (dopus_curwin[win]->realdevice[0] &&
												(testlock=Lock(dopus_curwin[win]->realdevice,ACCESS_READ))) {
												ret=CompareLock(lock,testlock);
												if (ret!=LOCK_DIFFERENT) data_window_refresh|=1<<win;
												UnLock(testlock);
											}
										}
										UnLock(lock);
									}
								}
							}
							break;
					}
					ReplyMsg((struct Message *)dopusmsg);
				}
				Wait(1<<disk_func.replyport->mp_SigBit|1<<control_port->mp_SigBit);
			}
			if (newscreen) CloseScreen(screen);
			LFreeRemember(&memory);
		}

		close_external(&disk_func,0);
		external_mod_segment[SEG_DISK]=disk_func.segment;
		LDeletePort(control_port);
		retcode=0;
	}

	my_startup_message->command=retcode;
	Forbid();
	ReplyMsg((struct Message *)my_startup_message);
	return;
}

void dopus_print(rexx,arglist,printdir,port,vdata)
int rexx;
struct DOpusArgsList *arglist;
int printdir;
char *port;
struct ViewData *vdata;
{
	char *args[16],portname[21],arglistbuf[20],funcpath[80];
	struct dopus_func_start print_func;
	int a,argcount,waitbits,abase=3;

	if (!vdata) dostatustext(globstring[STR_STARTING_PRINT_MODULE]);

	print_func.segment=external_mod_segment[SEG_PRINT];
	print_func.procname=external_modules[SEG_PRINT];

	strcpy(funcpath,external_modules[SEG_PRINT]);

	if (!print_func.segment)
		FindSystemFile(external_modules[SEG_PRINT],funcpath,80,SYSFILE_MODULE);

	print_func.segname=funcpath;

	args[0]=print_func.segname;

	StrCombine(portname,"&",port,20);
	args[1]=portname;

	if (printdir) {
		int win;

		if (!rexx || rexx_argcount<1 ||
			(win=atoi(rexx_args[0]))<0 || win>1) win=data_active_window;
		lsprintf(arglistbuf,"@%ld",win);
		abase=2;
		--rexx_argcount;
	}
	else lsprintf(arglistbuf,"!%ld",arglist);

	args[2]=arglistbuf;
	argcount=3;

	if (rexx && rexx_argcount>0) {
		if ((argcount+=rexx_argcount)>16) argcount=16;
		for (a=3;a<argcount;a++) args[a]=rexx_args[a-abase];
	}

	for (a=argcount;a<16;a++) args[a]="";

	print_func.argcount=argcount;
	print_func.args=args;
	print_func.stack=5000;
	print_func.flags=(config->loadexternal&LOAD_PRINT)?FF_SAVESEG:0;

	if (!(start_external(&print_func))) {
		close_external(&print_func,0);
		if (!vdata) dostatustext(globstring[STR_UNABLE_TO_LOAD_MODULE]);
		return;
	}

	waitbits=1<<print_func.replyport->mp_SigBit;
	if (vdata) waitbits|=1<<vdata->view_port->mp_SigBit;
	else waitbits|=rexx_signalbit;

	FOREVER {
		if ((Wait(waitbits))&rexx_signalbit && !vdata) {
			rexx_dispatch(0);
			continue;
		}
		if (vdata) {
			struct DOpusMessage *dmsg;
			struct DOpusArgsList *arg;

			while (dmsg=(struct DOpusMessage *)GetMsg(vdata->view_port)) {
				switch (dmsg->command) {
					case DOPUSMSG_GETVIS:
						CopyMem((char *)&vdata->view_vis_info,(char *)dmsg->data,
							sizeof(struct VisInfo));
						break;
					case DOPUSMSG_GETNEXTFILE:
						arg=(struct DOpusArgsList *)dmsg->data;
						if (arg->single_file) {
							strcpy(arg->file_data,arg->single_file);
							arg->single_file=NULL;
						}
						else arg->file_data[0]=0;
						break;
				}
				ReplyMsg((struct Message *)dmsg);
			}
		}
		if (GetMsg(print_func.replyport)) break;
	}

	close_external(&print_func,0);
	external_mod_segment[SEG_PRINT]=print_func.segment;

	if (!vdata) okay();
}

dopus_iconinfo(filename)
char *filename;
{
	char *args[3],portname[14],funcpath[80];
	struct dopus_func_start icon_func;
	int a;

	icon_func.segment=external_mod_segment[SEG_ICON];
	icon_func.procname=external_modules[SEG_ICON];

	strcpy(funcpath,external_modules[SEG_ICON]);

	if (!icon_func.segment)
		FindSystemFile(external_modules[SEG_ICON],funcpath,80,SYSFILE_MODULE);

	icon_func.segname=funcpath;

	StrCombine(portname,"&",str_arexx_portname,13);

	args[0]=icon_func.segname;
	args[1]=portname;
	args[2]=filename;

	icon_func.argcount=3;
	icon_func.args=args;
	icon_func.stack=5000;
	icon_func.flags=(config->loadexternal&LOAD_ICON)?FF_SAVESEG:0;

	if (!(start_external(&icon_func))) {
		close_external(&icon_func,0);
		dostatustext(globstring[STR_UNABLE_TO_LOAD_MODULE]);
		return(-4);
	}

	FOREVER {
		if ((Wait(1<<icon_func.replyport->mp_SigBit|rexx_signalbit))&rexx_signalbit) {
			rexx_dispatch(0);
			continue;
		}
		if (GetMsg(icon_func.replyport)) break;
	}

	a=close_external(&icon_func,0);
	external_mod_segment[SEG_ICON]=icon_func.segment;
	return(a);
}

void setup_externals()
{
	int a;
	char funcbuf[256];
	APTR wsave;

	wsave=main_proc->pr_WindowPtr;
	main_proc->pr_WindowPtr=(APTR)-1;

	if (config->loadexternal&LOAD_CONFIG) {
		FindSystemFile("ConfigOpus",funcbuf,256,SYSFILE_MODULE);
		configopus_segment=LoadSeg(funcbuf);
	}
	else configopus_segment=NULL;

	for (a=0;a<3;a++) {
		if (config->loadexternal&(1<<a)) {
			FindSystemFile(external_modules[a],funcbuf,256,SYSFILE_MODULE);
			external_mod_segment[a]=LoadSeg(funcbuf);
		}
		else external_mod_segment[a]=NULL;
	}

	main_proc->pr_WindowPtr=wsave;
}

void fill_out_visinfo(vis,scr)
struct VisInfo *vis;
struct Screen *scr;
{
	if (scr && (!Window || Window->WScreen!=scr)) {
		vis->vi_flags&=~VISF_WINDOW;
		vis->vi_screen=scr;

		vis->vi_fg=config->requestfg;
		vis->vi_bg=config->requestbg;
		vis->vi_shine=config->gadgettopcol;
		vis->vi_shadow=config->gadgetbotcol;
		vis->vi_stringcol[0]=config->stringfgcol;
		vis->vi_stringcol[1]=config->stringbgcol;
		vis->vi_activestringcol[0]=config->stringselfgcol;
		vis->vi_activestringcol[1]=config->stringselbgcol;
	}
	else {
		if (vis->vi_flags&VISF_WINDOW) vis->vi_screen=(struct Screen *)Window;
		else if (Window) vis->vi_screen=Window->WScreen;
		else vis->vi_screen=NULL;

		vis->vi_fg=screen_pens[config->requestfg].pen;
		vis->vi_bg=screen_pens[config->requestbg].pen;
		vis->vi_shine=screen_pens[config->gadgettopcol].pen;
		vis->vi_shadow=screen_pens[config->gadgetbotcol].pen;
		vis->vi_stringcol[0]=screen_pens[config->stringfgcol].pen;
		vis->vi_stringcol[1]=screen_pens[config->stringbgcol].pen;
		vis->vi_activestringcol[0]=screen_pens[config->stringselfgcol].pen;
		vis->vi_activestringcol[1]=screen_pens[config->stringselbgcol].pen;
	}

	if (vis->vi_flags&VISF_8POINTFONT) vis->vi_font=scr_font[FONT_GENERAL];
	else vis->vi_font=scr_font[FONT_REQUEST];

	if (config->generalscreenflags&SCR_GENERAL_REQDRAG)
		vis->vi_flags|=VISF_BORDERS;
	else vis->vi_flags&=~VISF_BORDERS;

	vis->vi_language=config->language;
}
