#include <stdio.h>
#include <string.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <libraries/dos.h>
#include <intuition/intuition.h>
#include <hardware/blit.h>
#include <devices/keymap.h>
#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>

#include "main.h"
#include "keymap.h"

#define FKEY3           0x0052
#define RET_KEY         0x0044
#define ENTER_KEY       0x0043

#define NORMAL_HEIGHT   200
#define SETUP_LINES     17
#define HEAD_ADJUST     2
#define SET_LINES       (SETUP_LINES + HEAD_ADJUST)
#define FONT_WIDTH      10
#define FONT_HEIGHT     9
#define BASE_LINE       2

#define SET_COLS        3
#define SET_ADJUST      2
#define SET_COL_WIDTH   ((WIDTH / FONT_WIDTH) / SET_COLS)
#define WIN_WIDTH       ((SET_COL_WIDTH * FONT_WIDTH) * SET_COLS)
#define WIN_LEFT        ((WIDTH - WIN_WIDTH) / 2)
#define WIN_HEIGHT      ((SET_LINES * FONT_HEIGHT) + (2 * BASE_LINE))
#define WIN_TOP         ((NORMAL_HEIGHT - WIN_HEIGHT) / 2)

#define NA  7
#define CU  8
#define KI  9

struct so save = { NULL,"SAVE SETTINGS",NULL,SO_SAVE,0,1,0,0 };

struct so appl = { &save,"APPLICATION KEYPAD",(APTR)"\x1b=",SO_SETUP,0,3,0,1 };
struct so num = { &appl,"NUMERIC KEYPAD",(APTR)"\x1b>",SO_SETUP,0,4,0,1 };

struct so ins = { &num,"INSERT",(APTR)"\x9b""4h",SO_SETUP,0,6,0,2 };
struct so over = { &ins,"OVERWRITE",(APTR)"\x9b""4l",SO_SETUP,0,7,0,2 };

struct so cret = { &over,"LINEFEED",(APTR)"\x9b""20l",SO_SETUP,0,9,0,3 };
struct so newl = { &cret,"NEWLINE",(APTR)"\x9b""20h",SO_SETUP,0,10,0,3 };

struct so wrap = { &newl,"WRAP",(APTR)"\x9b?7h",SO_SETUP,0,12,0,4 };
struct so trunc = { &wrap,"TRUNCATE",(APTR)"\x9b?7l",SO_SETUP,0,13,0,4 };

struct so bit_7 = { &trunc,"7-BIT",(APTR)"\x1b F",SO_SETUP,0,15,0,5 };
struct so bit_8 = { &bit_7,"8-BIT",(APTR)"\x1b G",SO_SETUP,0,16,0,5 };

struct so mult = { &bit_8,"MULTINATIONAL",(APTR)"\x9b?42l",SO_SETUP,1,0,0,6 };
struct so nat = { &mult,"NATIONAL",(APTR)"\x9b?42h",SO_SETUP,1,1,0,6 };

struct so br_set = { &nat,"BRITISH",(APTR)"\x1b(A",SO_SETUP,1,3,0,NA };
struct so du_set = { &br_set,"DUTCH",(APTR)"\x1b(4",SO_SETUP,1,4,0,NA };
struct so fi_set = { &du_set,"FINNISH",(APTR)"\x1b(C",SO_SETUP,1,5,0,NA };
struct so fr_set = { &fi_set,"FRENCH",(APTR)"\x1b(R",SO_SETUP,1,6,0,NA };
struct so fc_set = { &fr_set,"FRENCH/CANADIAN",(APTR)"\x1b(Q",SO_SETUP,1,7,0,NA };
struct so ger_set = { &fc_set,"GERMAN",(APTR)"\x1b(K",SO_SETUP,1,8,0,NA };
struct so ita_set = { &ger_set,"ITALIAN",(APTR)"\x1b(Y",SO_SETUP,1,9,0,NA };
struct so nor_set = { &ita_set,"NORWEGIAN",(APTR)"\x1b(E",SO_SETUP,1,10,0,NA };
struct so spa_set = { &nor_set,"SPANISH",(APTR)"\x1b(Z",SO_SETUP,1,11,0,NA };
struct so swe_set = { &spa_set,"SWEDISH",(APTR)"\x1b(H",SO_SETUP,1,12,0,NA };
struct so swi_set = { &swe_set,"SWISS",(APTR)"\x1b(=",SO_SETUP,1,13,0,NA };

struct so curson = { &swi_set,"CURSOR ON",(APTR)"\x9b?25h",SO_SETUP,1,15,0,CU };
struct so cursoff = { &curson,"CURSOR OFF",(APTR)"\x9b?25l",SO_SETUP,1,16,0,CU };

struct so us_km = { &cursoff,"NORTH AMERICAN",(APTR)us_map,SO_KEYMAP,2,0,0,KI };
struct so dn_km = { &us_km,"DANISH",(APTR)dn_map,SO_KEYMAP,2,1,0,KI };
struct so fl_km = { &dn_km,"FLEMISH",(APTR)fl_map,SO_KEYMAP,2,2,0,KI };
struct so br_km = { &fl_km,"BRITISH",(APTR)br_map,SO_KEYMAP,2,3,0,KI };
struct so du_km = { &br_km,"DUTCH",(APTR)du_map,SO_KEYMAP,2,4,0,KI };
struct so fi_km = { &du_km,"FINNISH",(APTR)fi_map,SO_KEYMAP,2,5,0,KI };
struct so bf_km = { &fi_km,"BELGIAN/FRENCH",(APTR)bf_map,SO_KEYMAP,2,6,0,KI };
struct so cf_km = { &bf_km,"CANADIAN (FRENCH)",(APTR)cf_map,SO_KEYMAP,2,7,0,KI };
struct so ag_km = { &cf_km,"AUSTRIAN/GERMAN",(APTR)ag_map,SO_KEYMAP,2,8,0,KI };
struct so it_km = { &ag_km,"ITALIAN",(APTR)it_map,SO_KEYMAP,2,9,0,KI };
struct so no_km = { &it_km,"NORWEGIAN",(APTR)no_map,SO_KEYMAP,2,10,0,KI };
struct so sp_km = { &no_km,"SPANISH",(APTR)sp_map,SO_KEYMAP,2,11,0,KI };
struct so sw_km = { &sp_km,"SWEDISH",(APTR)sw_map,SO_KEYMAP,2,12,0,KI };
struct so sf_km = { &sw_km,"SWISS (FRENCH)",(APTR)sf_map,SO_KEYMAP,2,13,0,KI };
struct so sg_km = { &sf_km,"SWISS (GERMAN)",(APTR)sg_map,SO_KEYMAP,2,14,0,KI };
struct so ic_km = { &sg_km,"ICELANDIC",(APTR)ic_map,SO_KEYMAP,2,15,0,KI };
struct so po_km = { &ic_km,"PORTUGUESE",(APTR)po_map,SO_KEYMAP,2,16,0,KI };

struct so res = { &po_km,"RESET TERMINAL",(APTR)"\x9b""!p",SO_LOAD,0,0,0,0 };

char itxt[SET_COL_WIDTH + 1];
struct TextAttr txf =
    { (STRPTR)"topaz.font",FONT_HEIGHT,FS_NORMAL,FPF_ROMFONT };
struct IntuiText btx =
    { BACKGROUND_PEN,FOREGROUND_PEN,JAM2,0,0,&txf,(UBYTE *)itxt,NULL };

struct NewWindow setup_win = {
    WIN_LEFT,WIN_TOP,WIN_WIDTH,WIN_HEIGHT,BACKGROUND_PEN,BACKGROUND_PEN,
    RAWKEY,SMART_REFRESH|BORDERLESS|ACTIVATE|NOCAREREFRESH|RMBTRAP,
    NULL,NULL,NULL,NULL,NULL,0,0,0,0,CUSTOMSCREEN };

setup(struct console *con)
{
struct Window       *win;
struct so           *sop,*osop,*nsop;
struct IntuiMessage *msg;
char                **cpp;
SHORT               cnt;
USHORT              code;
static char         *head[] = { "Miscellaneous","Character set","Keymap" };

SetRast(&con->srp,0);
setup_win.Screen = con->scr;
if ((win = OpenWindow(&setup_win)) == NULL) return(0);
SetRast(win->RPort,FOREGROUND_PEN);
cpp = head;
for (cnt = 0; cnt < SET_COLS; cnt++) {
    strcpy(itxt,*cpp);
    PrintIText(win->RPort,&btx,
                (cnt * SET_COL_WIDTH + SET_ADJUST) * FONT_WIDTH,BASE_LINE);
    cpp++;
    }
sop = &res;
showtext(win,sop,TRUE);
sop = sop->next;
while (sop != NULL) {
    showtext(win,sop,FALSE);
    sop = sop->next;
    }
code = 0;
osop = nsop = &res;
while (code != FKEY3) {
    if (nsop != osop) {
        showtext(win,osop,FALSE);
        showtext(win,nsop,TRUE);
        osop = nsop;
        }
    WaitPort(win->UserPort);
    msg = (struct IntuiMessage *)GetMsg(win->UserPort);
    if (msg->Class == RAWKEY) code = msg->Code;
    else code = 0;
    ReplyMsg((struct Message *)msg);
    switch (code) {
        case RET_KEY:
        case ENTER_KEY:
            switch (nsop->type) {
                case SO_SAVE:
                    writesetup(con);
                    break;
                case SO_LOAD:
                    reset(con);
                    readsetup(con);
                    sop = &res;
                    showtext(win,sop,TRUE);
                    sop = sop->next;
                    while (sop != NULL) {
                        showtext(win,sop,FALSE);
                        sop = sop->next;
                        }
                    break;
                case SO_KEYMAP:
                    replace((ULONG **)nsop->setup);
                    activate(nsop);
                    sop = &res;
                    while (sop != NULL) {
                        if (sop->id == nsop->id) {
                            if (sop == nsop) showtext(win,sop,TRUE);
                            else showtext(win,sop,FALSE);
                            }
                        sop = sop->next;
                        }
                    break;
                case SO_SETUP:
                    strcpy(con->buf,(char *)nsop->setup);
                    interpret(con,strlen(con->buf));
                    sop = &res;
                    while (sop != NULL) {
                        if (sop->id == nsop->id) {
                            if (sop == nsop) showtext(win,sop,TRUE);
                            else showtext(win,sop,FALSE);
                            }
                        sop = sop->next;
                        }
                    break;
                default: ;
                }
            break;
        case CURSORUP:
            cnt = osop->row - 1;
            if (cnt == -1) cnt = SETUP_LINES - 1;
            nsop = NULL;
            while (nsop == NULL) {
                sop = &res;
                while (sop != NULL) {
                    if ((sop->col == osop->col) &&
                        (sop->row == cnt)) {
                        nsop = sop;
                        break;
                        }
                    sop = sop->next;
                    }
                cnt--;
                if (cnt == -1) cnt = SETUP_LINES - 1;
                }
            break;
        case CURSORLEFT:
            cnt = osop->col - 1;
            if (cnt == -1) cnt = SET_COLS - 1;
            nsop = NULL;
            while (nsop == NULL) {
                sop = &res;
                while (sop != NULL) {
                    if ((sop->col == cnt) &&
                        (sop->row == osop->row)) {
                        nsop = sop;
                        break;
                        }
                    sop = sop->next;
                    }
                cnt--;
                if (cnt == -1) cnt = SET_COLS - 1;
                }
            break;
        case CURSORRIGHT:
            cnt = osop->col + 1;
            if (cnt == SET_COLS) cnt = 0;
            nsop = NULL;
            while (nsop == NULL) {
                sop = &res;
                while (sop != NULL) {
                    if ((sop->col == cnt) &&
                        (sop->row == osop->row)) {
                        nsop = sop;
                        break;
                        }
                    sop = sop->next;
                    }
                cnt++;
                if (cnt == SET_COLS) cnt = 0;
                }
            break;
        case CURSORDOWN:
            cnt = osop->row + 1;
            if (cnt == SETUP_LINES) cnt = 0;
            nsop = NULL;
            while (nsop == NULL) {
                sop = &res;
                while (sop != NULL) {
                    if ((sop->col == osop->col) &&
                        (sop->row == cnt)) {
                        nsop = sop;
                        break;
                        }
                    sop = sop->next;
                    }
                cnt++;
                if (cnt == SETUP_LINES) cnt = 0;
                }
            break;
        default: ;
        }
    }
CloseWindow(win);
initcursor(con);
DRAWCURSOR(con->rp,&con->srp,con->row,con->col);
return(0);
}

showtext(struct Window *win,struct so *sop,BOOL negative)
{
register char   *cp;
USHORT          len;

if (sop->active) strcpy(itxt," \xbb");
else strcpy(itxt,"  ");
strcat(itxt,sop->name);
len = strlen(itxt);
cp = itxt + len;
while (len < SET_COL_WIDTH) {
    *cp++ = ' ';
    len++;
    }
*cp = '\0';
if (negative) {
    btx.FrontPen = FOREGROUND_PEN;
    btx.BackPen = BACKGROUND_PEN;
    }
PrintIText(win->RPort,&btx,
            (sop->col * SET_COL_WIDTH) * FONT_WIDTH,
            (sop->row + HEAD_ADJUST) * FONT_HEIGHT + BASE_LINE);
if (negative) {
    btx.FrontPen = BACKGROUND_PEN;
    btx.BackPen = FOREGROUND_PEN;
    }
return(0);
}

activate(struct so *asop)
{
register struct so *sop;

sop = &res;
while (sop != NULL) {
    if (sop == asop) sop->active = TRUE;
    else if (sop->id == asop->id) sop->active = FALSE;
    sop = sop->next;
    }
return(0);
}

readsetup(struct console *con)
{
register struct so  *sop;
FILEHANDLE          fp;
USHORT              len;
UBYTE               c;

if (fp = OPEN_FOR_READING(TERMINALSETUP)) {
    if (READ(fp,(char *)&c,1)) {
        sop = &res;
        while (sop != NULL) {
            if (sop->id == KI) {
                if (sop->row == c) {
                    replace((ULONG **)sop->setup);
                    sop->active = TRUE;
                    }
                else sop->active = FALSE;
                }
            sop = sop->next;
            }
        do {
            len = READ(fp,con->buf,BUFSIZE);
            if (len) interpret(con,len);
            } while (len == BUFSIZE);
        }
    CLOSEFILE(fp);
    }
else {
    replace((ULONG **)us_km.setup);
    activate(&us_km);
    }
return(0);
}

writesetup(struct console *con)
{
register struct so  *sop;
FILEHANDLE          fp;
USHORT              cnt;

if (fp = OPEN_FOR_WRITING(TERMINALSETUP)) {
    sop = &res;
    while (sop != NULL) {
        if ((sop->id == KI)&&(sop->active)) {
            WRITE(fp,(char *)&sop->row,1);
            break;
            }
        sop = sop->next;
        }
    cnt = NA;
    while (cnt > 0) {
        sop = &res;
        while (sop != NULL) {
            if ((sop->id == cnt)&&(sop->active)) {
                WRITE(fp,(char *)sop->setup,strlen((char *)sop->setup));
                break;
                }
            sop = sop->next;
            }
        cnt--;
        }
    CLOSEFILE(fp);
    }
return(0);
}
