/*

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"

struct MsgPort *arbiter_reply_port;
struct MsgPort *arbiter_msg_port=NULL;
struct ProcessStart *arbiter_seglist=NULL;
struct Message arbiter_startup;

install_arbiter()
{
    if (!(arbiter_reply_port=LCreatePort(NULL,0)) ||
        !(arbiter_seglist=AllocMem(sizeof(struct ProcessStart),MEMF_CLEAR|MEMF_PUBLIC)))
        return(0);

    arbiter_seglist->ps_NextSeg=NULL;
    arbiter_seglist->ps_JMP=0x4ef9;
    arbiter_seglist->ps_EntryPoint=(void *)arbiter_process;

    arbiter_startup.mn_Node.ln_Type=NT_MESSAGE;
    arbiter_startup.mn_Node.ln_Pri=0;
    arbiter_startup.mn_ReplyPort=arbiter_reply_port;
    arbiter_startup.mn_Length=(UWORD)sizeof(struct ArbiterMessage);

    if (!(arbiter_msg_port=(struct MsgPort *)
        CreateProc("dopus_arbiter",0,(long)arbiter_seglist>>2,4000))) return(0);

    PutMsg(arbiter_msg_port,&arbiter_startup);
    return(1);
}

void remove_arbiter()
{
    if (arbiter_msg_port) {
        arbiter_command(ARBITER_REMOVE,NULL,0);
        WaitPort(arbiter_reply_port);
        GetMsg(arbiter_reply_port);
    }

    if (arbiter_seglist) FreeMem(arbiter_seglist,sizeof(struct ProcessStart));
    if (arbiter_reply_port) LDeletePort(arbiter_reply_port);
}

arbiter_command(command,data,flags)
int command;
APTR data;
int flags;
{
    struct ArbiterMessage arbiter_msg;
    struct MsgPort command_port;

    CopyMem((char *)arbiter_reply_port,(char *)&command_port,sizeof(struct MsgPort));
    NewList(&command_port.mp_MsgList);

    arbiter_msg.msg.mn_Node.ln_Type=NT_MESSAGE;
    arbiter_msg.msg.mn_Node.ln_Pri=0;
    arbiter_msg.msg.mn_ReplyPort=&command_port;
    arbiter_msg.msg.mn_Length=(UWORD)sizeof(struct ArbiterMessage);
    arbiter_msg.command=command;
    arbiter_msg.data=data;
    arbiter_msg.flags=flags;
    PutMsg(arbiter_msg_port,(struct Message *)&arbiter_msg);
    WaitPort(&command_port);
    GetMsg(&command_port);

    return(arbiter_msg.command);
}

struct LaunchList {
    struct LaunchList *next;
    struct ProcessStart *seglist;
    struct ArbiterMessage launch_msg;
    struct ArbiterMessage *reply_msg;
    struct DOpusRemember *memory;
};

void __saveds arbiter_process()
{
    struct Process *my_process;
    struct Message *my_startup_message;
    struct ArbiterMessage *arb_msg,*remove_msg=NULL;
    struct LaunchList *first_launch=NULL,*launch,*launchpos;
    struct MsgPort *message_reply;
    char ret,remove=0;
    int wait_mask;

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

    wait_mask=1<<my_process->pr_MsgPort.mp_SigBit;

    if (message_reply=LCreatePort(NULL,0))
        wait_mask|=1<<message_reply->mp_SigBit;

    FOREVER {
        if (message_reply) {
            while (arb_msg=(struct ArbiterMessage *)GetMsg(message_reply)) {
                launch=first_launch;
                launchpos=NULL;
                while (launch) {
                    if (arb_msg==&launch->launch_msg) {
                        if (launchpos) launchpos->next=launch->next;
                        else first_launch=launch->next;
                        if (launch->reply_msg) {
                            launch->reply_msg->command=arb_msg->command;
                            ReplyMsg((struct Message *)launch->reply_msg);
                        }
                        LFreeRemember(&launch->memory);
                        FreeMem(launch,sizeof(struct LaunchList));
                        break;
                    }
                    launchpos=launch;
                    launch=launch->next;
                }
            }
            if (remove_msg && !first_launch) {
                ReplyMsg((struct Message *)remove_msg);
                break;
            }
        }
        while (arb_msg=(struct ArbiterMessage *)GetMsg(&my_process->pr_MsgPort)) {
            ret=0;
            switch (arb_msg->command) {
                case ARBITER_REMOVE:
                    if (first_launch) {
                        remove_msg=arb_msg;
                        arb_msg=NULL;
                    }
                    else remove=1;
                    break;
                case ARBITER_LAUNCH:
                    if (message_reply &&
                        (launch=AllocMem(sizeof(struct LaunchList),MEMF_CLEAR))) {
                        struct ArbiterLaunch *arb_launch;
                        struct MsgPort *port;

                        if ((launchpos=first_launch)) {
                            while (launchpos->next) launchpos=launchpos->next;
                        }
                        if (launchpos) launchpos->next=launch;
                        else first_launch=launch;

                        arb_launch=(struct ArbiterLaunch *)arb_msg->data;

                        launch->memory=arb_launch->launch_memory;

                        if (launch->seglist=LAllocRemember(&launch->memory,
                            sizeof(struct ProcessStart),MEMF_PUBLIC|MEMF_CLEAR)) {

                            launch->seglist->ps_JMP=0x4ef9;
                            launch->seglist->ps_EntryPoint=arb_launch->launch_code;

                            launch->launch_msg.msg.mn_Node.ln_Type=NT_MESSAGE;
                            launch->launch_msg.msg.mn_ReplyPort=message_reply;
                            launch->launch_msg.msg.mn_Length=sizeof(struct ArbiterMessage);
                        
                            launch->launch_msg.command=arb_msg->command;
                            launch->launch_msg.data=arb_launch->data;
                            launch->launch_msg.flags=arb_msg->flags;

                            if ((port=(struct MsgPort *)
                                CreateProc(arb_launch->launch_name,0,(long)launch->seglist>>2,4000))) {
                                PutMsg(port,(struct Message *)&launch->launch_msg);
                                if (arb_msg->flags&ARB_WAIT) {
                                    launch->reply_msg=arb_msg;
                                    arb_msg=NULL;
                                }
                                else ret=1;
                            }
                        }
                        else {
                            LFreeRemember(&launch->memory);
                            FreeMem(launch,sizeof(struct ProcessStart));
                        }
                    }
                    break;
            }
            if (arb_msg) {
                arb_msg->command=ret;
                ReplyMsg((struct Message *)arb_msg);
            }
        }
        if (remove) break;
        Wait(wait_mask);
    }

    if (message_reply) LDeletePort(message_reply);
    Forbid();
    ReplyMsg(my_startup_message);
    return;
}

struct Screen *open_subprocess_screen(title,font,memkey,pens)
char *title;
struct TextFont *font;
struct DOpusRemember **memkey;
short *pens;
{
    struct ExtNewScreen newscreen;
    struct TagItem *screentags;
    struct TextAttr *screenattr;
    struct Screen *screen;
    UWORD *screen_drawinfo;

    if (!(screenattr=LAllocRemember(memkey,sizeof(struct TextAttr),MEMF_CLEAR)) ||
        !(screentags=LAllocRemember(memkey,sizeof(struct TagItem)*5,MEMF_CLEAR)) ||
        !(screen_drawinfo=LAllocRemember(memkey,sizeof(scr_drawinfo),0)))
        return(NULL);

    CopyMem((char *)scr_drawinfo,(char *)screen_drawinfo,sizeof(scr_drawinfo));

    if (pens) {
        screen_drawinfo[DETAILPEN]=pens[ARB_PEN_DETAIL];
        screen_drawinfo[BLOCKPEN]=pens[ARB_PEN_BLOCK];
    }

    screentags[0].ti_Tag=SA_DisplayID;
    screentags[0].ti_Data=clone_screen(MainScreen,&newscreen);
    screentags[1].ti_Tag=SA_Pens;
    screentags[1].ti_Data=(ULONG)screen_drawinfo;
    screentags[2].ti_Tag=SA_AutoScroll;
    screentags[2].ti_Data=TRUE;
    screentags[3].ti_Tag=SA_Interleaved;
    screentags[3].ti_Data=TRUE;
    screentags[4].ti_Tag=TAG_END;
    screentags[4].ti_Data=0;

/*  if (system_version2)*/ {
        struct DimensionInfo dimbuf;
        int width,height;

        if ((GetDisplayInfoData(NULL,
            (char *)&dimbuf,
            sizeof(struct DimensionInfo),
            DTAG_DIMS,
            screentags[0].ti_Data))) {

            width=(dimbuf.TxtOScan.MaxX-dimbuf.TxtOScan.MinX)+1;
            height=(dimbuf.TxtOScan.MaxY-dimbuf.TxtOScan.MinY)+1;
            if (newscreen.Width>width) newscreen.Width=width;
            if (newscreen.Height>height) newscreen.Height=height;
        }
  }

    screenattr->ta_Name=font->tf_Message.mn_Node.ln_Name;
    screenattr->ta_YSize=font->tf_YSize;

    newscreen.LeftEdge=0;
    newscreen.TopEdge=0;
    if (pens) {
        newscreen.DetailPen=pens[ARB_PEN_DETAIL];
        newscreen.BlockPen=pens[ARB_PEN_BLOCK];
    }
    else {
        newscreen.DetailPen=0;
        newscreen.BlockPen=1;
    }
    newscreen.Type=CUSTOMSCREEN|NS_EXTENDED;
    newscreen.Font=screenattr;
    newscreen.DefaultTitle=title;
    newscreen.Gadgets=NULL;
    newscreen.Extension=screentags;
    newscreen.Depth=config->scrdepth;

    if (!(screen=OpenScreen((struct NewScreen *)&newscreen))) return(NULL);
    load_palette(screen,config->new_palette);
    return(screen);
}
