/* NetHack 3.6	winstr.c	$NHDT-Date: 1432512795 2015/05/25 00:13:15 $  $NHDT-Branch: master $:$NHDT-Revision: 1.7 $ */
/* Copyright (c) Gregg Wonderly, Naperville, Illinois,  1991,1992,1993. */
/* NetHack may be freely redistributed.  See license for details. */

#ifndef CROSS_TO_AMIGA
#include "NH:sys/amiga/windefs.h"
#include "NH:sys/amiga/winext.h"
#include "NH:sys/amiga/winproto.h"
#else
#include "windefs.h"
#include "winext.h"
#include "winproto.h"
#endif
/* Put a string into the indicated window using the indicated attribute */

void
amii_putstr(winid window, int attr, const char *str)
{
    int fudge;
    int len;
    struct Window *w;
    register struct amii_WinDesc *cw;
    char *ob;
    int i, j, n0, bottom, totalvis, wheight;
    static int wrapping = 0;


    /* Always try to avoid a panic when there is no window */
    if (window == WIN_ERR) {
        window = WIN_BASE;
        if (window == WIN_ERR)
            window = WIN_BASE = amii_create_nhwindow(NHW_BASE);
    }

    if (window == WIN_ERR || (cw = amii_wins[window]) == NULL) {
        iflags.window_inited = 0;
        panic(winpanicstr, window, "putstr");
    }

    w = cw->win;

    if (!str)
        return;
    amiIDisplay->lastwin = window; /* do we care??? */

    /* NHW_MENU windows are not opened immediately, so check if we
     * have the window pointer yet
     */

    if (w) {
        /* Set the drawing mode and pen colors */
        SetDrMd(w->RPort, JAM2);
        amii_sethipens(w, cw->type, attr);
    } else if (cw->type != NHW_MENU && cw->type != NHW_TEXT) {
        panic("NULL window pointer in putstr 2: %d", window);
    }

    /* Okay now do the work for each type */

    switch (cw->type) {
    case NHW_MESSAGE:
        if (WINVERS_AMIV)
            fudge = 2;
        else {
            /* 8 for --more--, 1 for preceeding sp, 1 for putstr pad */
            fudge = 10;
        }

        /* There is a one pixel border at the borders, so subtract two */
        bottom = amii_msgborder(w);

        wheight = (w->Height - w->BorderTop - w->BorderBottom - 3)
                  / w->RPort->TxHeight;

        if (scrollmsg || wheight > 1)
            fudge = 0;

        amii_scrollmsg(w, cw);

        while (isspace(*str))
            str++;
        strncpy(gt.toplines, str, TBUFSZ);
        gt.toplines[TBUFSZ - 1] = 0;

        /* For initial message to be visible, we need to explicitly position
         * the
         * cursor.  This flag, cw->curx == -1 is set elsewhere to force the
         * cursor to be repositioned to the "bottom".
         */
        if (cw->curx == -1) {
            amii_curs(WIN_MESSAGE, 1, bottom);
            cw->curx = 0;
        }

        /* If used all of history lines, move them down */
        if (cw->maxrow >= iflags.msg_history) {
            if (cw->data[0])
                free(cw->data[0]);
            memcpy(cw->data, &cw->data[1],
                   (iflags.msg_history - 1) * sizeof(char *));
            cw->data[iflags.msg_history - 1] =
                (char *) alloc(strlen(gt.toplines) + SOFF + 4);
            strcpy(cw->data[i = iflags.msg_history - 1] + SOFF
                       + (scrollmsg != 0),
                   gt.toplines);
        } else {
            /* Otherwise, allocate a new one and copy the line in */
            cw->data[cw->maxrow] = (char *) alloc(strlen(gt.toplines) + SOFF + 4);
            strcpy(cw->data[i = cw->maxrow++] + SOFF + (scrollmsg != 0),
                   gt.toplines);
        }
        cw->data[i][SEL_ITEM] = 1;
        cw->data[i][VATTR] = attr + 1;

        if (scrollmsg) {
            cw->curx = 0;
            cw->data[i][2] = (cw->wflags & FLMSG_FIRST) ? '>' : ' ';
        }

        str = cw->data[i] + SOFF;
        if (cw->curx + strlen(str) >= (cw->cols - fudge)) {
            int i;
            char *ostr = (char *) str;
            char *p;

            while (cw->curx + strlen(str) >= (cw->cols - fudge)) {
                for (p = ((char *) &str[cw->cols - 1 - cw->curx]) - fudge;
                     !isspace(*p) && p > str;)
                    --p;
                if (p < str)
                    p = (char *) str;

                if (p == str) {
                    /*    p = (char *)&str[ cw->cols ]; */
                    outmore(cw);
                    continue;
                }

                i = (long) p - (long) str;
                outsubstr(cw, (char *) str, i, fudge);
                cw->curx += i;

                while (isspace(*p))
                    p++;
                str = p;

#if 0
		if( str != ostr ) {
		    outsubstr( cw, "+", 1, fudge );
		    cw->curx+=2;
		}
#endif
                if (*str)
                    amii_scrollmsg(w, cw);
                amii_cl_end(cw, cw->curx);
            }

            if (*str) {
                if (str != ostr) {
                    outsubstr(cw, "+", 1, fudge);
                    cw->curx += 2;
                }
                while (isspace(*str))
                    ++str;
                outsubstr(cw, (char *) str, i = strlen((char *) str), fudge);
                cw->curx += i;
                amii_cl_end(cw, cw->curx);
            }
        } else {
            outsubstr(cw, (char *) str, i = strlen((char *) str), fudge);
            cw->curx += i;
            amii_cl_end(cw, cw->curx);
        }
        cw->wflags &= ~FLMSG_FIRST;
        len = 0;
        if (scrollmsg) {
            totalvis = CountLines(window);
            SetPropInfo(w, &MsgScroll,
                        (w->Height - w->BorderTop - w->BorderBottom)
                            / w->RPort->TxHeight,
                        totalvis, totalvis);
        }
        i = strlen(gt.toplines + SOFF);
        cw->maxcol = max(cw->maxcol, i);
        cw->vwy = cw->maxrow;
        break;

    case NHW_STATUS:
        if (cw->data[cw->cury] == NULL)
            panic("NULL pointer for status window");
        ob = &cw->data[cw->cury][j = cw->curx];
        if (disp.botlx)
            *ob = 0;

        /* Display when beam at top to avoid flicker... */
        WaitTOF();
        {   int slen = strlen((char *) str);
            if (slen > cw->cols) slen = cw->cols;
            Text(w->RPort, (char *) str, slen);
        }
        if (cw->cols > strlen(str))
            TextSpaces(w->RPort, cw->cols - strlen(str));

        (void) strncpy(cw->data[cw->cury], str, cw->cols);
        cw->data[cw->cury][cw->cols - 1] = '\0'; /* null terminate */
        cw->cury = (cw->cury + 1) % 2;
        cw->curx = 0;
        break;

    case NHW_MAP:
    case NHW_BASE:
        if (cw->type == NHW_BASE && wrapping) {
            amii_curs(window, cw->curx + 1, cw->cury);
            TextSpaces(w->RPort, cw->cols);
            if (cw->cury < cw->rows) {
                amii_curs(window, cw->curx + 1, cw->cury + 1);
                TextSpaces(w->RPort, cw->cols);
                cw->cury--;
            }
        }
        amii_curs(window, cw->curx + 1, cw->cury);
        Text(w->RPort, (char *) str, strlen((char *) str));
        cw->curx = 0;
        /* CR-LF is automatic in these windows */
        cw->cury++;
        if (cw->type == NHW_BASE && cw->cury >= cw->rows) {
            cw->cury = 0;
            wrapping = 1;
        }
        break;

    case NHW_MENU:
    case NHW_TEXT:

        /* always grows one at a time, but alloc 12 at a time */

        if (cw->cury >= cw->rows || !cw->data) {
            char **tmp;

            /* Allocate 12 more rows */
            cw->rows += 12;
            tmp = (char **) alloc(sizeof(char *) * cw->rows);

            /* Copy the old lines */
            for (i = 0; i < cw->cury; i++)
                tmp[i] = cw->data[i];

            if (cw->data) {
                free(cw->data);
                cw->data = NULL;
            }

            cw->data = tmp;

            /* Null out the unused entries. */
            for (i = cw->cury; i < cw->rows; i++)
                cw->data[i] = 0;
        }

        if (!cw->data)
            panic("no data storage");

        /* Shouldn't need to do this, but... */

        if (cw->data && cw->data[cw->cury]) {
            free(cw->data[cw->cury]);
            cw->data[cw->cury] = NULL;
        }

        n0 = strlen(str) + 1;
        cw->data[cw->cury] = (char *) alloc(n0 + SOFF);

        /* avoid nuls, for convenience */
        cw->data[cw->cury][VATTR] = attr + 1;
        cw->data[cw->cury][SEL_ITEM] = 0;
        Strcpy(cw->data[cw->cury] + SOFF, str);

        if (n0 > cw->maxcol)
            cw->maxcol = n0;
        if (++cw->cury > cw->maxrow)
            cw->maxrow = cw->cury;
        break;

    default:
        panic("Invalid or unset window type in putstr()");
    }
}

void
amii_scrollmsg(struct Window *w, struct amii_WinDesc *cw)
{
    int bottom, wheight;

    bottom = amii_msgborder(w);

    wheight =
        (w->Height - w->BorderTop - w->BorderBottom - 3) / w->RPort->TxHeight;

    if (scrollmsg) {
        if (++cw->disprows > wheight) {
            outmore(cw);
            cw->disprows = 1; /* count this line... */
        } else {
            ScrollRaster(w->RPort, 0, w->RPort->TxHeight, w->BorderLeft,
                         w->BorderTop + 1, w->Width - w->BorderRight - 1,
                         w->Height - w->BorderBottom - 1);
        }
        amii_curs(WIN_MESSAGE, 1, bottom);
    }
}

int
amii_msgborder(struct Window *w)
{
    register int bottom;

    /* There is a one pixel border at the borders, so subtract two */
    bottom = w->Height - w->BorderTop - w->BorderBottom - 2;
    bottom /= w->RPort->TxHeight;
    if (bottom > 0)
        --bottom;
    return (bottom);
}

void
outmore(struct amii_WinDesc *cw)
{
    struct Window *w = cw->win;

    if ((cw->wflags & FLMAP_SKIP) == 0) {
        if (scrollmsg) {
            int bottom;

            bottom = amii_msgborder(w);

            ScrollRaster(w->RPort, 0, w->RPort->TxHeight, w->BorderLeft,
                         w->BorderTop + 1, w->Width - w->BorderRight - 1,
                         w->Height - w->BorderBottom - 1);
            amii_curs(WIN_MESSAGE, 1, bottom); /* -1 for inner border */
            Text(w->RPort, "--more--", 8);
        } else
            Text(w->RPort, " --more--", 9);

        /* Make sure there are no events in the queue */
        flushIDCMP(HackPort);

        /* Allow mouse clicks to clear --more-- */
        WindowGetchar();
        if (lastevent.type == WEKEY && lastevent.un.key == '\33')
            cw->wflags |= FLMAP_SKIP;
    }
    if (!scrollmsg) {
        amii_curs(WIN_MESSAGE, 1, 0);
        amii_cl_end(cw, cw->curx);
    }
}

void
outsubstr(struct amii_WinDesc *cw, char *str, int len, int fudge)
{
    struct Window *w = cw->win;

    if (cw->curx) {
        /* Check if this string and --more-- fit, if not,
         * then put out --more-- and wait for a key.
         */
        if ((len + fudge) + cw->curx >= cw->cols) {
            if (!scrollmsg)
                outmore(cw);
        } else {
            /* Otherwise, move and put out a blank separator */
            Text(w->RPort, spaces, 1);
            cw->curx += 1;
        }
    }

    Text(w->RPort, str, len);
}

/* Put a graphics character onto the screen */

void
amii_putsym(winid st, int i, int y, CHAR_P c)
{
    amii_curs(st, i, y);
    Text(amii_wins[st]->win->RPort, &c, 1);
}

/* Add to the last line in the message window */

void
amii_addtopl(const char *s)
{
    register struct amii_WinDesc *cw = amii_wins[WIN_MESSAGE];

    while (*s) {
        if (cw->curx == cw->cols - 1)
            amii_putstr(WIN_MESSAGE, 0, "");
        amii_putsym(WIN_MESSAGE, cw->curx + 1, amii_msgborder(cw->win), *s++);
        cw->curx++;
    }
}

void
TextSpaces(struct RastPort *rp, int nr)
{
    if (nr < 1)
        return;

    while (nr > sizeof(spaces) - 1) {
        Text(rp, spaces, (long) sizeof(spaces) - 1);
        nr -= sizeof(spaces) - 1;
    }
    if (nr > 0)
        Text(rp, spaces, (long) nr);
}

void
amii_remember_topl(void)
{
    /* ignore for now.  I think this will be done automatically by
     * the code writing to the message window, but I could be wrong.
     */
}

int
amii_doprev_message(void)
{
    struct amii_WinDesc *cw;
    struct Window *w;
    char *str;


    if (WIN_MESSAGE == WIN_ERR || (cw = amii_wins[WIN_MESSAGE]) == NULL
        || (w = cw->win) == NULL) {
        panic(winpanicstr, WIN_MESSAGE, "doprev_message");
    }

    /* When an interlaced/tall screen is in use, the scroll bar will be there
     */
    /* Or in some other cases as well */
    if (scrollmsg) {
        struct Gadget *gd;
        struct PropInfo *pip;
        int hidden, topidx, i, total, wheight;

        for (gd = w->FirstGadget; gd && gd->GadgetID != 1;)
            gd = gd->NextGadget;

        if (gd) {
            pip = (struct PropInfo *) gd->SpecialInfo;
            wheight = (w->Height - w->BorderTop - w->BorderBottom - 2)
                      / w->RPort->TxHeight;
            hidden = max(cw->maxrow - wheight, 0);
            topidx = (((ULONG) hidden * pip->VertPot) + (MAXPOT / 2)) >> 16;
            for (total = i = 0; i < cw->maxrow; ++i) {
                if (cw->data[i][1] != 0)
                    ++total;
            }

            i = 0;
            topidx -= wheight / 4 + 1;
            if (topidx < 0)
                topidx = 0;
            SetPropInfo(w, &MsgScroll, wheight, total, topidx);
            DisplayData(WIN_MESSAGE, topidx);
        }
        return (0);
    }

    if (--cw->vwy < 0) {
        cw->maxcol = 0;
        DisplayBeep(NULL);
        str = "\0\0No more history saved...";
    } else
        str = cw->data[cw->vwy];

    amii_cl_end(cw, 0);
    amii_curs(WIN_MESSAGE, 1, 0);
    amii_setdrawpens(amii_wins[WIN_MESSAGE]->win, NHW_MESSAGE);
    Text(w->RPort, str + SOFF, strlen(str + SOFF));
    cw->curx = cw->cols + 1;

    return (0);
}
