/* mod_dos.c
 * Copyright (C) 1990 Commodore-Amiga, Inc.
 * written by David N. Junod
 *
 * DOS command shell message handling routines
 *
 */

#include "mod.h"

/* variables required for a DOS shell */
struct DOSInfo
{
    struct MsgPort *drport;	/* DOS reply port */
    UBYTE *winspec;		/* Shell window spec */
    UBYTE *closing_msg;		/* Closing message */
    BPTR acons;			/* Console file handle */
    struct StandardPacket *dmsg;/* DOS packet */
    UBYTE aprompt[25];		/* prompt */
    BOOL packet_out;		/* is a READ outstanding? */
    UBYTE buff[BUFFLEN + 1];	/* used for reading user input */
    struct Process *atask;	/* Our own process structure */
    BPTR old_CIS;		/* Orig. console input stream */
    BPTR old_COS;		/* Orig. console output stream */
    APTR old_ConsoleTask;	/* Orig. console task */
    SHORT dstatus;		/* The current status of the shell */
};

/* DOS message handling routines */
BOOL open_dos (struct AppInfo *, struct MsgHandler *mh);
BOOL handle_dos (struct AppInfo *, struct MsgHandler *mh);
BOOL close_dos (struct AppInfo *, struct MsgHandler *mh);
BOOL shutdown_dos (struct AppInfo *, struct MsgHandler *mh);
VOID DisplayPrompt ( struct MsgHandler *mh );
struct StandardPacket *setup_dos_message ( void );
VOID send_read_packet (struct StandardPacket *,BPTR,struct MsgPort *, UBYTE *);

struct MsgHandler *setup_dos (struct AppInfo * ai,
			      UBYTE * winspec, UBYTE * prompt, UBYTE * close,
			      BOOL immed)
{
    struct MsgHandler *mh = NULL;
    struct DOSInfo *md = NULL;

    if (md = (struct DOSInfo *)
	AllocMem (sizeof (struct DOSInfo), MEMF_CLEAR|MEMF_PUBLIC))
    {
	/* initialize data */
	md->closing_msg = close;
	md->winspec = winspec;
	strcpy (md->aprompt, prompt);

	/* get the current process information */
	md->atask = (struct Process *) FindTask (0);

	/* make backups of the default task information */
	md->old_CIS = md->atask->pr_CIS;
	md->old_COS = md->atask->pr_COS;
	md->old_ConsoleTask = md->atask->pr_ConsoleTask;
	md->packet_out = FALSE;

	/* Mark shell window as being closed */
	md->dstatus = AS_CLOSED;

	/* set up a port for DOS replys */
	if (md->drport = CreatePort (NULL, 0L))
	{
	    /* set up a DOS packet for the asynchronous read from the window */
	    if (md->dmsg = setup_dos_message ())
	    {
		mh = (struct MsgHandler *)
		    AllocMem (sizeof (struct MsgHandler), MEMF_CLEAR|MEMF_PUBLIC);
		if (mh)
		{
		    mh->mh_Node.ln_Type = MH_HANDLER_T;
		    mh->mh_Node.ln_Pri  = MH_HANDLER_P;
		    mh->mh_Node.ln_Name = "DOS";
		    mh->mh_SigBits = (1L << md->drport->mp_SigBit);
		    mh->mh_Func[MH_OPEN] = open_dos;
		    mh->mh_Func[MH_HANDLE] = handle_dos;
		    mh->mh_Func[MH_CLOSE] = close_dos;
		    mh->mh_Func[MH_SHUTDOWN] = shutdown_dos;
		    mh->mh_Data = md;

		    /* open immediately? */
		    if (immed)
		    {
			if (open_dos (ai, mh))
			    return (mh);
		    }
		    else
			return (mh);

		    FreeMem (mh, sizeof (struct MsgHandler));
		}
		else
		    NotifyUser (NULL, "Not enough memory");

		FreeMem (md->dmsg, sizeof (struct StandardPacket));
		md->dmsg = NULL;
	    }
	    DeletePort (md->drport);
	    md->drport = NULL;
	}
	FreeMem (md, sizeof (struct DOSInfo));
    }
    return (mh);
}

BOOL open_dos (struct AppInfo * ai, struct MsgHandler * mh)
{
    struct DOSInfo *md = mh->mh_Data;
    struct FileHandle *handle;

    if (!md->acons)
    {
	if (md->acons = Open (md->winspec, MODE_NEWFILE))
	{
	    /* Set the standard input/output to this console window */
	    md->atask->pr_CIS = md->acons;
	    md->atask->pr_COS = md->acons;

	    /* Convert the BPTR to a C pointer */
	    handle = (struct FileHandle *) (md->acons << 2);

	    /* Set the console task in case they open a window off ours */
	    md->atask->pr_ConsoleTask = (APTR) handle->fh_Type;

	    /* Display the prompt */
	    DisplayPrompt (mh);
	    md->dstatus = AS_OPEN;

	    /* send a packet out for user input */
	    if (!md->packet_out)
	    {
		/* send a packet to DOS asking for user keyboard input */
		send_read_packet (md->dmsg, md->acons, md->drport, md->buff);
		md->packet_out = TRUE;
	    }

	}
    }
    return ((BOOL) ((md->acons) ? TRUE : FALSE));
}

/* handle DOS messages */
BOOL handle_dos (struct AppInfo * ai, struct MsgHandler * mh)
{
    struct DOSInfo *md = mh->mh_Data;

    if (GetMsg (md->drport))
    {
	/* not out any more */
	md->packet_out = FALSE;

	/* If the shell CLOSE gadget was hit, or ^\ */
	if (md->dmsg->sp_Pkt.dp_Res1 == 0)
	{
	    md->dstatus = AS_GOING;
	}
	else
	{
	    md->buff[md->dmsg->sp_Pkt.dp_Res1 - 1] = 0;

	    if (strlen (md->buff) > 0)
	    {
		/* perform the function */
		PerfFunc (ai, NULL, md->buff);

		/* display the prompt */
		DisplayPrompt (mh);
	    }
	    else
		/* they entered a blank line, let's end the shell */
		md->dstatus = AS_GOING;
	}
	/* send out a packet for user input */
	if (!md->packet_out && md->dstatus >= AS_OPEN)
	{
	    /* send a packet to DOS asking for user keyboard input */
	    send_read_packet (md->dmsg, md->acons, md->drport, md->buff);
	    md->packet_out = TRUE;
	}
	close_dos (ai, mh);
    }
    return (TRUE);
}

BOOL close_dos (struct AppInfo * ai, struct MsgHandler * mh)
{
    struct DOSInfo *md = mh->mh_Data;

    if (md->dstatus == AS_GOING)
    {
	/* User has indicated that he wants to close the command shell. */
	md->dstatus = AS_CLOSING;
	Write (md->acons, md->closing_msg, (LONG) strlen (md->closing_msg));
    }

    if (md->dstatus == AS_CLOSING && !ai->numcmds)
    {
	if (md->acons)
	{
	    /* safely restore the default information */
	    md->atask->pr_CIS = md->old_CIS;
	    md->atask->pr_COS = md->old_COS;
	    md->atask->pr_ConsoleTask = md->old_ConsoleTask;

	    /* close the console window */
	    Close (md->acons);
	}

	md->dstatus = AS_CLOSED;
	md->acons = NULL;
    }
    return ((BOOL) ((md->acons) ? TRUE : FALSE));
}

BOOL shutdown_dos (struct AppInfo * ai, struct MsgHandler * mh)
{
    struct DOSInfo *md = mh->mh_Data;

    if (mh)
    {
	if (md)
	{
	    if (md->acons)
		md->dstatus = AS_GOING;
	    close_dos (ai, mh);

	    if (md->drport)
		DeletePort (md->drport);

	    if (md->dmsg)
		FreeMem (md->dmsg, sizeof (struct StandardPacket));
	}

	Remove ((struct Node *) mh);
	FreeMem (mh, sizeof (struct MsgHandler));
    }

    return (TRUE);
}

VOID DisplayPrompt (struct MsgHandler * mh)
{
    struct DOSInfo *md = mh->mh_Data;

    Write (md->acons, md->aprompt, (LONG) strlen (md->aprompt));
}

struct StandardPacket *setup_dos_message ()
{
    struct StandardPacket *new_packet;

    /* get a packet */
    if (new_packet = (struct StandardPacket *)
	AllocMem (sizeof (struct StandardPacket), MEMF_CLEAR|MEMF_PUBLIC))
    {
	new_packet->sp_Msg.mn_Node.ln_Name =
	  (UBYTE *) & (new_packet->sp_Pkt);
	new_packet->sp_Pkt.dp_Link = &(new_packet->sp_Msg);
    }
    return (new_packet);
}

VOID send_read_packet (struct StandardPacket * dos_message,
		        BPTR console_fh,
		        struct MsgPort * dos_reply_port,
		        UBYTE * buff)
{
    struct FileHandle *file_handle;

    /* change a BPTR to a REAL pointer */
    file_handle = (struct FileHandle *) (console_fh << 2);

    /* setup the packet for reading */
    dos_message->sp_Pkt.dp_Arg1 = file_handle->fh_Arg1;
    dos_message->sp_Pkt.dp_Arg2 = (LONG) buff;
    dos_message->sp_Pkt.dp_Arg3 = BUFFLEN;
    dos_message->sp_Pkt.dp_Type = ACTION_READ;
    dos_message->sp_Pkt.dp_Port = dos_reply_port;
    dos_message->sp_Msg.mn_ReplyPort = dos_reply_port;

    /* now send it */
    PutMsg (file_handle->fh_Type, (struct Message *) dos_message);
}
