/*
 * Amiga system-dependent routines. 
 */

#include <proto/exec.h>
#include <proto/dos.h>
#include <exec/memory.h>
#include <devices/conunit.h>
#include <stdio.h>
#include <ios1.h>
#include <error.h>

#include "stevie.h"

/* Globals initialized by get_ConUnit() */
struct Window  *conWindow;
struct ConUnit *conUnit;

extern int      errno;		/* The error variable */

long            raw_in = 0;
long            raw_out = 0;

#define BSIZE   2048
static char     outbuf[BSIZE];
static int      bpos = 0;

void
flushbuf()
{
    if (bpos != 0)
	Write(raw_out, outbuf, bpos);
    bpos = 0;
}

void
outchar(c)
    char            c;
{
    outbuf[bpos++] = c;
    if (bpos >= BSIZE)
	flushbuf();
}

void
outstr(s)
    char           *s;
{
    while (*s) {
	outbuf[bpos++] = *s++;
	if (bpos >= BSIZE)
	    flushbuf();
    }
}

int
GetCharacter()
{
    char            c;

    Read(raw_in, &c, sizeof(c));
    return ((int) c);
}

/*
 * getCSIsequence - get a CSI sequence
 *                - either cursor keys, help, or function keys
 */

int
getCSIsequence()
{
    int             c;
    int             tmp;


    c = GetCharacter();
    if (isdigit(c)) {
	tmp = 0;
	while (isdigit(c)) {
	    tmp = tmp * 10 + c - '0';
	    c = GetCharacter();
	}
	if (c == '~')		/* function key */
	    return ((char) (K_F1 + tmp));
    }
    switch (c) {
      case 'A':		/* cursor up */
	return K_UARROW;
      case 'B':		/* cursor down */
	return K_DARROW;
      case 'C':		/* cursor right */
	return K_RARROW;
      case 'D':		/* cursor left */
	return K_LARROW;
      case 'T':		/* shift cursor up */
	return K_SUARROW;
      case 'S':		/* shift cursor down */
	return K_SDARROW;
      case ' ':		/* shift cursor left or right */
	c = GetCharacter();
	if (c == 'A')		/* shift cursor left */
	    return K_SLARROW;
	if (c == '@')		/* shift cursor right */
	    return K_SRARROW;
	break;
      case '?':		/* help */
	c = GetCharacter();
	if (c == '~')
	    return K_HELP;
	break;
    }
    while ((c != '|') && (c != '~')) {
	if (WaitForChar(raw_in, 500L) == 0)
	    break;
	c = GetCharacter();
    }

    /* must have been screen resize event */
    s_clear();
    flushbuf();
    if (get_ConUnit(raw_in) != 0) {	/* hopefully never exit .... */
	emsg("STEVIE: can't get ConUnit info ?!?!?!?\n");
	sleep(5);
	return 0;
    }
    Rows = conUnit->cu_YMax + 1;
    Columns = conUnit->cu_XMax + 1;
    if (Columns < 5)
	Columns = 5;
    if (Columns > MAX_COLUMNS)
	Columns = MAX_COLUMNS;
    if (Rows < 2)
	Rows = 2;
    P(P_LI) = Rows;

    screenalloc();
    tmp = RedrawingDisabled;
    RedrawingDisabled = TRUE;
    S_NOT_VALID;
    cursupdate(UPDATE_ALL);	/* make sure not below Botchar */
    RedrawingDisabled = FALSE;
    s_refresh(NOT_VALID);	/* draw it */
    RedrawingDisabled = tmp;
    windgoto(Cursrow, Curscol);
    flushbuf();

    return 0;
}

/*
 * inchar() - get a character from the keyboard 
 */
int
inchar()
{
    int             c;

    flushbuf();

    for (;;) {
	c = GetCharacter();
	if (c == 0x9b)
	    c = getCSIsequence();
	if (c != 0)
	    break;
    }

    return c;
}

void
beep()
{
    if (RedrawingDisabled)
	return;

    outbuf[bpos++] = '\007';
    if (bpos >= BSIZE)
	flushbuf();
}

void
sleep(n)
    int             n;
{
    void            Delay();

    if (n > 0)
	Delay(50L * n);
}

void
delay()
{
    void            Delay();

    Delay(25L);
}

void
windinit()
{
    raw_in = Input();
    if (!IsInteractive(raw_in)) {
	raw_in = Open("RAW:0/0/480/200/STEVIE", MODE_NEWFILE);
	if (raw_in == NULL) {
	    fprintf(stderr, "STEVIE: Can't open window ?!?!?!?\n");
	    exit(2);
	}
	raw_out = raw_in;
    } else {
	raw_out = Output();
	if (raw(raw_in) != 0) {
	    perror("STEVIE: Can't change to raw mode ?!?!?!?");
	    exit(2);
	}
    }

    if (get_ConUnit(raw_in) != 0) {
	fprintf(stderr, "STEVIE: can't get ConUnit info ?!?!?!?\n");
	windexit(3);
    }
    /* get window size */
    P(P_LI) = Rows = conUnit->cu_YMax + 1;
    Columns = conUnit->cu_XMax + 1;

    outstr("\033[12{");		/* window resize events activated */
    flushbuf();
}

void
windexit(r)
    int             r;
{
    outstr("\033[12}");		/* window resize events de-activated */
    flushbuf();

    if (raw_in != raw_out) {
	if (cooked(raw_in) != 0)
	    perror("STEVIE: Can't change to cooked mode ?!?!?!?");
    } else {
	Close(raw_in);
    }

    exit(r);
}

void
windgoto(r, c)
    int             c;
    int             r;
{
    r++;
    c++;

    outstr("\033[");
    if (r >= 10)
	outchar((char) (r / 10 + '0'));
    outchar((char) (r % 10 + '0'));
    outchar(';');
    if (c >= 10)
	outchar((char) (c / 10 + '0'));
    outchar((char) (c % 10 + '0'));
    outchar('H');
}

FILE           *
fopenb(fname, mode)
    char           *fname;
    char           *mode;
{
    FILE           *fopen();
    char            modestr[16];

    sprintf(modestr, "%sb", mode);
    return fopen(fname, modestr);
}

/*
 * raw() & cooked()
 *
 * These are routines for setting a given stream to raw or cooked mode on the
 * Amiga. This is useful when you are using Lattice C to produce programs
 * that want to read single characters with the "getch()" or "fgetc" call. 
 *
 * Written : 18-Jun-87 By Chuck McManis.
 */

/*
 * Function raw() - Convert the specified File Handle to 'raw' mode. This
 * only works on TTY's and essentially keeps DOS from translating keys for
 * you.
 */

long
raw(afh)
    struct FileHandle *afh;
{
    struct MsgPort *mp;		/* The File Handle message port */
    long            Arg[1], res;

    mp = ((struct FileHandle *) (BADDR(afh)))->fh_Type;
    Arg[0] = -1L;
    res = SendPacket(mp, ACTION_SCREEN_MODE, Arg, 1);
    if (res == 0) {
	errno = ENXIO;
	return (-1);
    }
    return (0);
}

/*
 * Function - cooked() this function returns the designate file pointer to
 * it's normal, wait for a <CR> mode. This is exactly like raw() except that
 * it sends a 0 to the console to make it back into a CON: from a RAW: 
 */

long
cooked(afh)
    struct FileHandle *afh;
{
    struct MsgPort *mp;		/* The File Handle message port */
    long            Arg[1], res;

    mp = ((struct FileHandle *) (BADDR(afh)))->fh_Type;
    Arg[0] = 0L;
    res = SendPacket(mp, ACTION_SCREEN_MODE, Arg, 1);
    if (res == 0) {
	errno = ENXIO;
	return (-1);
    }
    return (0);
}

/*
 * Code for this routine came from the following :
 *
 * ConPackets.c -  C. Scheppner, A. Finkel, P. Lindsay  CBM
 *   DOS packet example
 *   Requires 1.2
 *
 * which I found on Fish Disk 56.
 */

/* initializes conWindow and conUnit (global vars) */
long
get_ConUnit(afh)
    struct FileHandle *afh;
{
    struct MsgPort *mp;		/* The File Handle message port */
    struct InfoData *id;
    long            Arg[8], res;

    if (!IsInteractive((BPTR) afh)) {
	errno = ENOTTY;
	return (-1);
    }
    mp = ((struct FileHandle *) (BADDR(afh)))->fh_Type;

    /* Alloc to insure longword alignment */
    id = (struct InfoData *) AllocMem(sizeof(struct InfoData),
				      MEMF_PUBLIC | MEMF_CLEAR);
    if (!id) {
	errno = ENOMEM;
	return (-1);
    }
    Arg[0] = ((ULONG) id) >> 2;
    res = SendPacket(mp, ACTION_DISK_INFO, Arg, 1);
    conWindow = (struct Window *) id->id_VolumeNode;
    conUnit = (struct ConUnit *) ((struct IOStdReq *) id->id_InUse)->io_Unit;
    FreeMem(id, sizeof(struct InfoData));
    if (res == 0) {
	errno = ENXIO;
	return (-1);
    }
    return (0);
}

/*
 * SendPacket() - written by Phil Lindsay, Carolyn Scheppner, and Andy
 * Finkel. This function will send a packet of the given type to the Message
 * Port supplied. 
 */

long
SendPacket(pid, action, args, nargs)
    struct MsgPort *pid;	/* process indentifier ... (handlers message
				 * port ) */
    long            action,	/* packet type ... (what you want handler to
				 * do )   */
                    args[],	/* a pointer to a argument list */
                    nargs;	/* number of arguments in list  */
{
    struct MsgPort *replyport;
    struct StandardPacket *packet;

    long            count, *pargs, res1;

    replyport = (struct MsgPort *) CreatePort(NULL, 0);
    if (!replyport)
	return (0);

    /* Allocate space for a packet, make it public and clear it */
    packet = (struct StandardPacket *)
	AllocMem((long) sizeof(struct StandardPacket),
		 MEMF_PUBLIC | MEMF_CLEAR);
    if (!packet) {
	DeletePort(replyport);
	return (0);
    }
    packet->sp_Msg.mn_Node.ln_Name = (char *) &(packet->sp_Pkt);
    packet->sp_Pkt.dp_Link = &(packet->sp_Msg);
    packet->sp_Pkt.dp_Port = replyport;
    packet->sp_Pkt.dp_Type = action;

    /* copy the args into the packet */
    pargs = &(packet->sp_Pkt.dp_Arg1);	/* address of first argument */
    for (count = 0; count < nargs; count++)
	pargs[count] = args[count];

    PutMsg(pid, packet);	/* send packet */

    WaitPort(replyport);
    GetMsg(replyport);

    res1 = packet->sp_Pkt.dp_Res1;

    FreeMem(packet, (long) sizeof(struct StandardPacket));
    DeletePort(replyport);

    return (res1);
}
