// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 1997-2007 Ralf S. Engelschall.

#include "config.h"
#include "iselect.h"

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <wctype.h>
#include <curses.h>

typedef struct CustomKey {
    enum iSelect_key in;
    enum iSelect_key out;
} CustomKey;
static CustomKey*KeyList;
static size_t   nKeyList;

typedef struct keydef {
    const char *str;
    enum iSelect_key key;
} keydef;
static const keydef KeyDef[] = {
    { "SPACE",     L' ' },
    { "RETURN",    L'\n' },
    { "KEY_DOWN",  iSelect_NextLine },
    { "KEY_UP",    iSelect_PrevLine },
    { "KEY_NPAGE", iSelect_NextPage },
    { "KEY_PPAGE", iSelect_PrevPage },
    { "KEY_LEFT",  iSelect_Left },
    { "KEY_RIGHT", iSelect_Right },
};

void name_key(enum iSelect_key key, FILE *into)
{
    if (key == iSelect_DontKnow){
        fputs("UNKNOWN", into);
        return;
    }
    for (size_t i = 1 /* skip SPACE! */; i < sizeof(KeyDef) / sizeof(*KeyDef); ++i)
        if (KeyDef[i].key == key) {
            fputs(KeyDef[i].str, into);
            return;
        }
    fprintf(into, "%lc", (wchar_t)key);
}

static enum iSelect_key asc2key(const char *str)
{
    for (size_t i = 0; i < sizeof(KeyDef) / sizeof(*KeyDef); ++i)
        if (!strcmp(KeyDef[i].str, str))
                return KeyDef[i].key;

    wchar_t k;
    int parsed = mbtowc(&k, str, strlen(str));
    if (parsed <= 0 || (size_t)parsed != strlen(str) || !iswprint(k))
        fprintf(stderr, "iSelect: unknown key %s\n", str), exit(1);
    return k;

}

void configure_custom_key(char *config)
{
    const char *in, *out;
    char *cp;

    if ((cp = strchr(config, ':')) != NULL) {
        *cp = '\0';
        in  = config;
        out = cp + 1;
    }
    else {
        in  = config;
        out = "RETURN";
    }

    if (!(KeyList = reallocarray(KeyList, ++nKeyList, sizeof(*KeyList))))
        fprintf(stderr, "iSelect: %s\n", strerror(errno)), exit(1);
    KeyList[nKeyList - 1].in  = asc2key(in);
    KeyList[nKeyList - 1].out = asc2key(out);
}

enum iSelect_key mapped_key(WINDOW *win, enum iSelect_key *unmapped_key)
{
    wint_t ret;
    switch (wget_wch(win, &ret)) {
        case ERR:
        default:
            return *unmapped_key = iSelect_DontKnow;

        case OK:
            // no mapping needed
            break;

        case KEY_CODE_YES:
            switch (ret) {
                case KEY_DOWN:  ret = iSelect_NextLine; break;
                case KEY_UP:    ret = iSelect_PrevLine; break;
                case KEY_NPAGE: ret = iSelect_NextPage; break;
                case KEY_PPAGE: ret = iSelect_PrevPage; break;
                case KEY_LEFT:  ret = iSelect_Left;     break;
                case KEY_RIGHT: ret = iSelect_Right;    break;
                default:        ret = iSelect_DontKnow; break;
            }
    }
    *unmapped_key = ret;

    for (size_t i = 0; i < nKeyList; ++i)
        if ((wint_t)KeyList[i].in == ret)
            ret = KeyList[i].out;
    if (ret == iSelect_Left)
        ret = L'q';
    if (ret == iSelect_Right || ret == L'\r')
        ret = L'\n';
    return ret;
}
