/*

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;
}
