/*************************************************************************
 ***                        win.c                        (JJB TEMPLAR) ***
 *** Date begun: 8/8/89.                                               ***
 *** Last modified: 26/8/89.                                           ***
 *************************************************************************/
/*** Intuition/Console device stuff for ty. These functions are        ***
 *** currently used by io.c, ttyin.c, and output.c.                    ***
 *** Most of the console code is lifted directly from the chapter on   ***
 *** the console.device in the RKM:L&D.                                ***
 *** Too easy to use IDCMP rather than console for input, so console   ***
 *** input has been dropped. At the moment, this means multi-char      ***
 *** commands have also been dropped.                                  ***
 *************************************************************************/

#include <exec/types.h>
#include <exec/io.h>
#include <devices/conunit.h>
#include <devices/inputevent.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/dos.h>

#include "cmd.h"
#include "gadg.h"

#define SHIFT       (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
#define CONTROL     (IEQUALIFIER_CONTROL)
#define MAXHORIZ    60          /* Max value for l_start */

extern char version[],sc_lower_left[];
extern struct Gadget *fgadg;
extern int  tty;                    /* Use as flag that cons is open */
extern int  sc_width,sc_height;     /* Resize them here. */
extern int  l_start;                /* Change this one here too. */
extern int  has_resized;            /* Set if getc() notices NEWSIZE */
extern int  maxim;
extern BPTR file;

extern struct cmdmap uraw[];
extern struct cmdmap lraw[];
extern struct cmdmap lgad[];
extern struct cmdmap rgad[];

extern void flush();
extern void cleanup(char *,int);
extern int  mapcmd(UBYTE,struct cmdmap *);

extern int  rbut(struct IntuiMessage *);

struct Window       *Window;
struct NewWindow    NewWindow = {
    1,1,639,199,0,1,CLOSEWINDOW|RAWKEY|GADGETUP|NEWSIZE|MOUSEBUTTONS,
    WINDOWSIZING|WINDOWDEPTH|WINDOWCLOSE|WINDOWDRAG|SMART_REFRESH|ACTIVATE|RMBTRAP,
    NULL,NULL,&version[0],NULL,NULL,250,109,1024,1024,WBENCHSCREEN};

struct IOStdReq *conWmsg;
struct MsgPort  *conWport;
struct ConUnit  *cunit;     /* Get at console parameters */

int     opencon();
void    tOpen();
void    tWrite(UBYTE *,int);
void    tClose();
void    texit(int,char *);
int     getcmd();
void    resize();
void    doboom();
void    movewin(int,int,int,int,int);
void    horiz(int);
int     linecount();
int     conpos();           /* Return console x pos */
int     getclick(struct IntuiMessage *);

int     opencon() /*=====================================================*/
{
register int    ret;
    conWmsg->io_Data = (APTR)Window;
    conWmsg->io_Length = sizeof(struct Window);
    ret = OpenDevice("console.device",0,conWmsg,0);
    return(ret);
}

void    tOpen() /*=======================================================*/
{
    if (!(conWport = CreatePort("ty.conW",0)))
                        texit(0,"ERROR: failed to open write port!\n");
    if (!(conWmsg = CreateStdIO(conWport)))
                        texit(1,"ERROR: failed to open write message!\n");
    NewWindow.FirstGadget = fgadg;
    if (!(Window = OpenWindow(&NewWindow)))
                        texit(2,"ERROR: failed to open window!\n");
    if (opencon())      texit(3,"ERROR: failed to open console.device!\n");

    cunit = (struct ConUnit *)conWmsg->io_Unit;
    cunit->cu_Modes[2] = 0x30;      /* Turn off auto-wrap */

    tty = 123L;         /* Used as flag, until other code updated */
    tWrite("[0 p",5); /* Turn off cursor */
    SetWindowTitles(Window,(char *)-1L,version);
}

void    tWrite(cp,sz) /*=================================================*/
UBYTE   *cp;
int     sz;
{
    conWmsg->io_Command = CMD_WRITE;
    conWmsg->io_Data = (APTR)cp;
    conWmsg->io_Length = sz;
    DoIO(conWmsg);
}

void    tClose() /*======================================================*/
{
    if (!tty) return;           /* Check flag */
    tty = NULL;                 /* Reset flag. */
    CloseDevice(conWmsg);       /* In reverse order to that in which     */
    CloseWindow(Window);        /* they were procured. */
    DeleteStdIO(conWmsg);
    DeletePort(conWport);
}

void    texit(bra,cp) /*=================================================*/
int     bra;
char    *cp;
{
    switch (bra) {
        case (3): CloseWindow(Window);
        case (2): DeleteStdIO(conWmsg);
        case (1): DeletePort(conWport);
        case (0): break;
    }
    cleanup(cp,20);
}

int     getcmd() /*======================================================*/
{                /* Get a command from the IDCMP.                        */
struct IntuiMessage mvl;
register struct IntuiMessage *msg;
register int    ret = C_UNKNOWN;
register int    loop = 1;

    if (maxim) {            /* Do here in case maxim set by file icon */
        maxim = 0;          /* check instead of when ty started.      */
        return(C_BOOM);
    }

    flush();        /* Sync display */
    while (loop) {
        loop = 0;   /* Set back to one, to loop again. */
        Wait(1 << Window->UserPort->mp_SigBit);
        while (msg = (struct IntuiMessage *)GetMsg(Window->UserPort)) {
            mvl = *msg;
            ReplyMsg((struct Message *)msg);
            switch (mvl.Class) {
                case (CLOSEWINDOW): ret = C_QUIT;   break;
                case (RAWKEY):
                    if (mvl.Qualifier & CONTROL) switch (mvl.Code) {
                        case (0x28): ret = C_RESIZE;    break;  /* L */
                        default: loop = 1;
                    }
                    else if (mvl.Qualifier & SHIFT) {
                        if ((ret = mapcmd((UBYTE)mvl.Code,uraw)) == C_UNKNOWN) loop = 1;
                    }
                    else {
                        if ((ret = mapcmd((UBYTE)mvl.Code,lraw)) == C_UNKNOWN) loop = 1;
                    }
		    break;
                case (GADGETUP):
                    ret = mapcmd((UBYTE)(((struct Gadget *)(mvl.IAddress))->GadgetID),lgad);
                    break;
                case (MOUSEBUTTONS): 
                    if (mvl.Code == MENUUP) {
                        if ((ret = mapcmd((UBYTE)rbut(&mvl),rgad)) == C_UNKNOWN) loop = 1;
                    }
                    else if (mvl.Code == SELECTDOWN) {
                        ret = getclick(&mvl);
                        if (ret == C_UNKNOWN) loop = 1;
                    }
                    else loop = 1;              /* REST */
                    break;
                case (NEWSIZE): ret = C_RESIZE; break;
            }
        }
    }

    return(ret);
}

int     getc() /*========================================================*/
{              /* Get a key from the IDCMP.                              */
register struct IntuiMessage *msg;
register int    ret = -1;

    while (ret < 0) {
        Wait(1 << Window->UserPort->mp_SigBit);
        while (msg = (struct IntuiMessage *)GetMsg(Window->UserPort)) {
            if (msg->Class == RAWKEY) ret = msg->Code;
          /* So clicking a gadget will be equiv to ... (press RETURN) */
            else if (msg->Class & (GADGETUP|GADGETDOWN|CLOSEWINDOW)) ret = 0x44;
            else if (msg->Class == NEWSIZE) has_resized = 1;
            ReplyMsg((struct Message *)msg);
        }
        if ((ret >= 0x80) && (ret <= 0xf8)) ret = -1; /* Ignore up trans */
    }
    return(ret);
}

void    resize() /*======================================================*/
{
    tWrite("[t[u",5);                   /* Get console to resize */

    sc_width = (int)cunit->cu_XMax + 1;     /* Then get from there */
    sc_height = (int)cunit->cu_YMax + 2;

  /* Update control string to go to lower left. Much faster if it's updated
   * only once every screen resize. */
    sprintf(sc_lower_left,"[%d;1H",sc_height);

    SetAPen(Window->RPort,0);
    RectFill(Window->RPort,2,11,Window->Width - 17,Window->Height - 10);
}

void    movewin(x,y,w,h,s) /*============================================*/
int     x,y,w,h,s;
{
int     tx,ty,tw,th;

    tx = x - Window->LeftEdge;  ty = y - Window->TopEdge;
    tw = w - Window->Width;     th = h - Window->Height;

    if (s) {
        MoveWindow(Window,tx,ty);
        SizeWindow(Window,tw,th);
    }
    else {
        SizeWindow(Window,tw,th);
        MoveWindow(Window,tx,ty);
    }
}

void    doboom() /*======================================================*/
{
static int  state = 0,x,y,w,h;

    if (!state) {
        state = 1;
        x = Window->LeftEdge;   y = Window->TopEdge;
        w = Window->Width;      h = Window->Height;

        movewin(0,0,Window->WScreen->Width,Window->WScreen->Height,state);
    }
    else {
        state = 0;
        movewin(x,y,w,h,state);
    }
}

void    horiz(f) /*======================================================*/
int     f;
{
    if (f) {        /* Move right. */
        if (l_start < MAXHORIZ-10) l_start += 10;
        else l_start = MAXHORIZ;
    }
    else {
        if (l_start > 10) l_start -= 10;
        else l_start = 0;
    }
    sethbar();
}

int     linecount() /*===================================================*/
{                   /* Return number of lines in "file".                 */
char    buf[512];   /* Can I spare ½ K of stack at this point? Hope so.  */
register int    ret = 0,j;
register char   *cp,*e;
ULONG   oldpos;
    if (!file) return(0);
    oldpos = Seek(file,0,OFFSET_BEGINNING);

    while ((j = Read(file,buf,512)) > 0)
        for (cp = buf,e = buf + j; cp < e; cp++)
            if (*cp == '\n') ret++;

    Seek(file,oldpos,OFFSET_BEGINNING);
    return(ret);
}

int     conpos() /*======================================================*/
{
    return((int)cunit->cu_XCCP);
}

int     getclick(msg) /*=================================================*/
struct IntuiMessage *msg;
{
register int    x,y;

    x = msg->MouseX;    y = msg->MouseY;
    if ((x < 2) || (x > Window->Width-18) || (y < 11) || (y > Window->Height-10)) return(C_UNKNOWN);
    return((y < (Window->Height >> 1))? C_BACK_PAGE: C_FORW_PAGE);
}
