/*
 * tngp/ui_input.c
 *
 * Copyright (C) 2022 bzt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * @brief Text input
 *
 */

#include "tngp.h"

#define NUMKBD 11

typedef struct {
    char *id;
    int num;
    const char **keys;
} kbd_t;

/* IMPORTANT: only symbols which do not interfere with filenames, paths or URLs allowed */
const char *abc_numbers[] ={ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "_", "-", "+", ".", "。", "$", "¢", "£", "¤", "¥",
    "§", "©", "®", "ª", "°", "¹", "²", "³", "½", "¼", "¾", "'", "/", NULL };
const char *abc_capital[] ={ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
    "U", "V", "W", "X", "Y", "Z", "ß", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ā", "Ă", "Ą", "Ç", "Ć", "Ĉ", "Ċ", "Č", "Ð", "Ď", "Đ",
    "È", "É", "Ê", "Ë", "Ē", "Ĕ", "Ė", "Ę", "Ě", "Ĝ", "Ğ", "Ġ", "Ģ", "Ĥ", "Ħ", "Ì", "Í", "Î", "Ï", "Ĩ", "Ī", "Ĭ", "Į", "İ", "Ĵ",
    "Ķ", "Ĺ", "Ļ", "Ľ", "Ŀ", "Ł", "Ñ", "Ń", "Ņ", "Ň", "Ŋ", "Ò", "Ó", "Ô", "Õ", "Ö", "Ø", "Ō", "Ŏ", "Ő", "Œ", "Ŕ", "Ŗ", "Ř", "Ś",
    "Ŝ", "Ş", "Š", "Ţ", "Ť", "Ŧ", "Ù", "Ú", "Û", "Ü", "Ũ", "Ū", "Ŭ", "Ů", "Ű", "Ų", "Ŵ", "Ý", "Ŷ", "Ÿ", "Þ", "Ź", "Ż", "Ž", NULL };
const char *abc_lower[] =  { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
    "u", "v", "w", "x", "y", "z", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ā", "ă", "ą", "ç", "ć", "ĉ", "ċ", "č", "ð", "ď", "đ",
    "è", "é", "ê", "ë", "ē", "ĕ", "ė", "ę", "ě", "ĝ", "ğ", "ġ", "ģ", "ĥ", "ħ", "ì", "í", "î", "ï", "ĩ", "ī", "ĭ", "į", "ı", "ĵ",
    "ķ", "ĺ", "ļ", "ľ", "ŀ", "ł", "ñ", "ń", "ņ", "ň", "ŋ", "ò", "ó", "ô", "õ", "ö", "ø", "ō", "ŏ", "ő", "œ", "ŕ", "ŗ", "ř", "ś",
    "ŝ", "ş", "š", "ţ", "ť", "ŧ", "ù", "ú", "û", "ü", "ũ", "ū", "ŭ", "ů", "ű", "ų", "ŵ", "ý", "ŷ", "ÿ", "þ", "ź", "ż", "ž", NULL };
const char *abc_greekc[] = { "Α", "Β", "Γ", "Δ", "Ε", "Ζ", "Η", "Θ", "Ι", "Κ", "Λ", "Μ", "Ν", "Ξ", "Ο", "Π", "Ρ", "Σ", "Τ", "Υ",
    "Φ", "Χ", "Ψ", "Ω", "Ͱ", "Ͳ", "Ͷ", "Ϙ", "Ϛ", "Ϝ", "Ϟ", "Ϡ", "Ϣ", "Ϥ", "Ϧ", "Ϩ", "Ϫ", "Ϭ", "Ϯ", "Ϳ", "Ϻ", "Ͻ", "Ͼ", "Ͽ", "Ϸ",
    "Ά", "Έ", "Ή", "Ί", "Ϊ", "Ϋ", "Ό", "Ύ", "Ώ", "ϒ", "ϓ", "ϔ", NULL };
const char *abc_greekl[] = { "α", "β", "γ", "δ", "ε", "ζ", "η", "θ", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ρ", "σ", "τ", "υ",
    "φ", "χ", "ψ", "ω", "ͱ", "ͳ", "ͷ", "ϙ", "ϛ", "ϝ", "ϟ", "ϡ", "ϣ", "ϥ", "ϧ", "ϩ", "ϫ", "ϭ", "ϯ", "ϳ", "ϻ", "ͻ", "ͼ", "ͽ", "ϸ",
    "ά", "έ", "ή", "ί", "ϊ", "ϋ", "ό", "ύ", "ώ", "ΐ", "ΰ", "ς", NULL };
const char *abc_cyrillc[] ={ "А", "Б", "В", "Г", "Д", "Е", "Ж", "З", "И", "Й", "К", "Л", "М", "Н", "О", "П", "Р", "С", "Т", "У",
    "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ", "Ы", "Ь", "Э", "Ю", "Я", "Ѐ", "Ё", "Ђ", "Ѓ", "Є", "Ѕ", "І", "Ї", "Ј", "Љ", "Њ", "Ћ", "Ќ",
    "Ѝ", "Ў", "Џ", "Ѡ", "Ѣ", "Ѥ", "Ѧ", "Ѩ", "Ѫ", "Ѭ", "Ѯ", "Ѱ", "Ѳ", "Ѵ", "Ѷ", "Ѹ", "Ѻ", "Ѽ", "Ѿ", "Ҁ", NULL };
const char *abc_cyrilll[] ={ "а", "б", "в", "г", "д", "е", "ж", "з", "и", "й", "к", "л", "м", "н", "о", "п", "р", "с", "т", "у",
    "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ы", "ь", "э", "ю", "я", "ѐ", "ё", "ђ", "ѓ", "є", "ѕ", "і", "ї", "ј", "љ", "њ", "ћ", "ќ",
    "ѝ", "ў", "џ", "ѡ", "ѣ", "ѥ", "ѧ", "ѩ", "ѫ", "ѭ", "ѯ", "ѱ", "ѳ", "ѵ", "ѷ", "ѹ", "ѻ", "ѽ", "ѿ", "ҁ", NULL };
const char *abc_hebrew[] = { "א", "ב", "ג", "ד", "ה", "ו", "ז", "ח", "ט", "י", "ך", "כ", "ל", "ם", "מ", "ן", "נ", "ס", "ע", "ף",
    "פ", "ץ", "צ", "ק", "ר", "ש", "ת", NULL };
const char *abc_hira[] ={ "ぁ", "あ", "ぃ", "い", "ぅ", "う", "ぇ", "え", "ぉ", "お", "か", "が", "き", "ぎ", "く", "ぐ", "け", "げ",
    "こ", "ご", "さ", "ざ", "し", "じ", "す", "ず", "せ", "ぜ", "そ", "ぞ", "た", "だ", "ち", "ぢ", "っ", "つ", "づ", "て", "で", "と",
    "ど", "な", "に", "ぬ", "ね", "の", "は", "ば", "ぱ", "ひ", "び", "ぴ", "ふ", "ぶ", "ぷ", "へ", "べ", "ぺ", "ほ", "ぼ", "ぽ", "ま",
    "み", "む", "め", "も", "ゃ", "や", "ゅ", "ゆ", "ょ", "よ", "ら", "り", "る", "れ", "ろ", "ゎ", "わ", "ゐ", "ゑ", "を", "ん", "ゔ",
    "ゕ", "ゖ", NULL };
const char *abc_kata[] ={ "ァ", "ア", "ィ", "イ", "ゥ", "ウ", "ェ", "エ", "ォ", "オ", "カ", "ガ", "キ", "ギ", "ク", "グ", "ケ", "ゲ",
    "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", "ゼ", "ソ", "ゾ", "タ", "ダ", "チ", "ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト",
    "ド", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "バ", "パ", "ヒ", "ビ", "ピ", "フ", "ブ", "プ", "ヘ", "ベ", "ペ", "ホ", "ボ", "ポ", "マ",
    "ミ", "ム", "メ", "モ", "ャ", "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ヮ", "ワ", "ヰ", "ヱ", "ヲ", "ン", "ヴ",
    "ヵ", "ヶ", "ヷ", "ヸ", "ヹ", "ヺ", "・", NULL };
const char *abc_devana[] = { "ऄ", "अ", "आ", "इ", "ई", "उ", "ऊ", "ऋ", "ऌ", "ऍ", "ऎ", "ए", "ऐ", "ऑ", "ऒ", "ओ", "औ", "क", "ख",
    "ग", "घ", "ङ", "च", "छ", "ज", "झ", "ञ", "ट", "ठ", "ड", "ढ", "ण", "त", "थ", "द", "ध", "न", "ऩ", "प", "फ", "ब", "भ", "म",
    "य", "र", "ऱ", "ल", "ळ", "ऴ", "व", "श", "ष", "स", "ह", "ॐ", "क़", "ख़", "ग़", "ज़", "ड़", "ढ़", "फ़", "य़", "ॠ", "ॡ", "ॲ", "ॳ",
    "ॴ", "ॵ", "ॶ", "ॷ", "ॸ", "ॹ", "ॺ", "ॻ", "ॼ", "ॽ", "ॾ", "ॿ", NULL };
kbd_t ui_keyboards[NUMKBD] = {
    { "0", 0, abc_numbers },
    { "A", 0, abc_capital },
    { "a", 0, abc_lower },
    { "Γ", 0, abc_greekc },
    { "γ", 0, abc_greekl },
    { "Б", 0, abc_cyrillc },
    { "б", 0, abc_cyrilll },
    { "א", 0, abc_hebrew },
    { "あ", 0, abc_hira },
    { "ア", 0, abc_kata },
    { "ऄ", 0, abc_devana }
};

SDL_Texture *inpbg = NULL;
int ui_input_maxlen, ui_input_kbd, ui_input_pass, ui_input_sel, ui_input_scr, ui_input_p = 0, ui_input_gp = 0, ui_input_cnt = 0;
char *ui_input_label = NULL, *ui_input_str = NULL, *ui_input_cur = NULL, *ui_input_end = NULL, ui_input_buf[1024];
SDL_Rect ui_input_rect, ui_input_d;

/**
 * Initialize text input
 */
void ui_input_init()
{
    int i, j;

    ui_input_kbd = 1;
    for(j = 0; j < NUMKBD; j++) {
        if(j && !strcmp(ui_keyboards[j].id, lang[DEFAULT_KBD])) ui_input_kbd = j;
        for(i = 0; ui_keyboards[j].keys[i]; i++);
        ui_keyboards[j].num = i;
    }
}

/**
 * Start text input
 */
void ui_input_start(int x, int y, int w, int h, char *label, char *str, int maxlen, int pass)
{
    int i, p;
    uint32_t *data;

    if(!str || maxlen < 1) return;
    memset(ui_input_buf, 0, sizeof(ui_input_buf));
    strncpy(ui_input_buf, str, sizeof(ui_input_buf) - 1);
    ui_input_label = label;
    ui_input_str = str;
    ui_input_maxlen = maxlen;
    ui_input_pass = pass;
    ui_input_cur = ui_input_end = str + strlen(str);
    ui_input_scr = ui_input_p = 0; ui_input_sel = NUMKBD;
    if(ui_input_gp) {
        ui_input_rect.x = ui_input_rect.y = 12; ui_input_rect.w = win_w - 24; ui_input_rect.h = 16;
        inpbg = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, win_w, win_h / 2);
        if(inpbg) {
            SDL_SetTextureBlendMode(inpbg, SDL_BLENDMODE_BLEND);
            SDL_LockTexture(inpbg, NULL, (void**)&data, &p);
            for(i = 0; i < win_w * win_h / 2; i++) data[i] = 0xCF000000;
            if(label && *label) {
                ssfn_dst.ptr = (uint8_t*)data;
                ssfn_dst.w = win_w - 256;
                ssfn_dst.h = win_h / 2;
                ssfn_dst.p = p;
                ssfn_dst.x = 8;
                ssfn_dst.y = 12;
                ssfn_dst.fg = 0xff606060;
                while(*label && ssfn_dst.x < ui_input_rect.w) ssfn_putc(ssfn_utf8(&label));
                ui_input_rect.x = ssfn_dst.x + 12;
                ui_input_rect.w = win_w - 12 - ui_input_rect.x;
            }
            SDL_UnlockTexture(inpbg);
        }
    } else {
        ui_input_rect.x = x; ui_input_rect.y = y; ui_input_rect.w = w; ui_input_rect.h = h;
        SDL_SetTextInputRect(&ui_input_rect);
        if(touch && !ui_input_cnt) { SDL_StartTextInput(); }
    }
    ui_input_cnt++;
}

/**
 * End text input
 */
void ui_input_stop()
{
    ui_input_str = ui_input_cur = ui_input_end = ui_input_label = NULL; ui_input_maxlen = 0;
    if(inpbg) { SDL_DestroyTexture(inpbg); inpbg = NULL; }
    if(touch && ui_input_cnt == 1) SDL_StopTextInput();
    ui_input_cnt--; if(ui_input_cnt < 0) ui_input_cnt = 0;
}

/**
 * Add an utf-8 key to the string
 */
void ui_input_addkey(char *key)
{
    int i, j, k = strlen(key);

    if((uint8_t)key[0] < ' ' || (ui_input_end - ui_input_str + k + 1) >= ui_input_maxlen) return;
    /* one special case: space isn't listed in any alphabet */
    if(key[0] == ' ') { if(ui_input_cur != ui_input_str) goto ok; else return; }
    /* in chat mode we allow any key, even the ones not on the keyboard */
    if(ui_input_pass == 2 && key[0] > ' ' && key[0] != 127) goto ok;
    /* in username input mode, even though '/' is on the keyboard, we don't allow it */
    if(!ui_input_pass && key[0] == '/') return;
    for(j = 0; j < NUMKBD; j++)
        for(i = 0; i < ui_keyboards[j].num; i++)
            if(!strcmp(key, ui_keyboards[j].keys[i])) {
ok:             memmove(ui_input_cur + k, ui_input_cur, ui_input_end - ui_input_cur + 1);
                memcpy(ui_input_cur, key, k);
                ui_input_cur += k; ui_input_end += k;
                return;
            }
}

/**
 * Text input box controller
 */
int ui_input_ctrl()
{
    int x, y, w, h, l;
    char *s;

    if(!ui_input_str || ui_input_maxlen < 1) return 0;
    y = ui_input_rect.y + ui_input_rect.h + 8; w = win_w / 12; h = (win_h / 2 - y) / 5;
    x = (win_w - w * NUMKBD) / 2; y += win_h / 2;
    switch(event.type) {
        case SDL_TEXTINPUT:
            if(!ui_input_gp || event.text.text[0] != ' ')
                ui_input_addkey((char*)&event.text.text);
        break;
        case SDL_KEYDOWN:
            switch(event.key.keysym.sym) {
                case SDLK_RETURN: ui_input_p = 1; ui_input_sel = NUMKBD + 40; break;
                case SDLK_SPACE: ui_input_p = 1; break;
            }
        break;
        case SDL_KEYUP:
            l = ui_input_p; ui_input_p = 0;
            switch(event.key.keysym.sym) {
                case SDLK_AC_BACK:
                case SDLK_F10:
                case SDLK_ESCAPE: strcpy(ui_input_str, ui_input_buf); ui_input_stop(); return 0;
                case SDLK_TAB:
                case SDLK_RETURN: ui_input_stop(); return 2;
                case SDLK_UP:
                    if(!ui_input_gp) { ui_input_stop(); return -1; }
up:                 if(ui_input_sel >= NUMKBD + 36) {
                        x = ui_input_sel - NUMKBD - 36;
                        ui_input_sel -= (x < 3 ? 12 : (x < 4 ? 7 : 6));
                    } else
                    if(ui_input_sel == NUMKBD + 11)
                        ui_input_sel = NUMKBD - 1;
                    else
                        ui_input_sel -= ui_input_sel - 12 < NUMKBD ? 11 : 12;
                    if(ui_input_sel >= NUMKBD && ui_input_sel < NUMKBD + 36) {
                        while(ui_input_sel - NUMKBD + ui_input_scr >= ui_keyboards[ui_input_kbd].num)
                            ui_input_sel -= 12;
                        if(ui_input_sel < NUMKBD)
                            ui_input_sel = NUMKBD + ui_keyboards[ui_input_kbd].num - ui_input_scr - 1;
                    }
                    if(ui_input_sel < 0 || ui_input_sel > NUMKBD + 40) ui_input_sel = NUMKBD + 40;
                break;
                case SDLK_DOWN:
                    if(!ui_input_gp) { ui_input_stop(); return 1; }
down:               if(ui_input_sel < NUMKBD + 36 && (ui_input_sel + 12 >= NUMKBD + 36 ||
                      ui_input_sel - NUMKBD + ui_input_scr + 12 >= ui_keyboards[ui_input_kbd].num)) {
                        x = (ui_input_sel - NUMKBD) % 12;
                        ui_input_sel = NUMKBD + 36 + (x < 2 ? x : (x < 8 ? 2 : (x < 10 ? 3 : 4)));
                    } else
                        ui_input_sel += ui_input_sel < NUMKBD ? 11 : 12;
                    if(ui_input_sel > NUMKBD + 40) ui_input_sel = 0;
                    if((ui_input_sel == NUMKBD + 36 || ui_input_sel == NUMKBD + 37) && ui_keyboards[ui_input_kbd].num <= 36)
                        ui_input_sel = NUMKBD + 38;
                    if(ui_input_sel >= NUMKBD && ui_input_sel < NUMKBD + 36 && ui_input_sel - NUMKBD + ui_input_scr >=
                      ui_keyboards[ui_input_kbd].num)
                        ui_input_sel = NUMKBD + ui_keyboards[ui_input_kbd].num - ui_input_scr - 1;
                break;
                case SDLK_LEFT:
                    if(!ui_input_gp) {
                        if(ui_input_cur > ui_input_str)
                            do { ui_input_cur--; } while(ui_input_cur > ui_input_str && (*ui_input_cur & 0xC0) == 0x80);
                        break;
                    }
left:               if(ui_input_sel > 0) ui_input_sel--; else ui_input_sel = NUMKBD + 40;
                    if(ui_input_sel < NUMKBD + 36 && ui_input_sel - NUMKBD + ui_input_scr >= ui_keyboards[ui_input_kbd].num)
                        ui_input_sel = NUMKBD + ui_keyboards[ui_input_kbd].num - ui_input_scr - 1;
                    if((ui_input_sel == NUMKBD + 36 || ui_input_sel == NUMKBD + 37) && ui_keyboards[ui_input_kbd].num <= 36)
                        ui_input_sel = ui_keyboards[ui_input_kbd].num + NUMKBD - 1;
                break;
                case SDLK_RIGHT:
                    if(!ui_input_gp) {
                        if(ui_input_cur < ui_input_end)
                            do { ui_input_cur++; } while(ui_input_cur < ui_input_end && (*ui_input_cur & 0xC0) == 0x80);
                        break;
                    }
right:              ui_input_sel++;
                    if(ui_input_sel > NUMKBD + 40) ui_input_sel = 0;
                    if(ui_input_sel < NUMKBD + 36 && ui_input_sel - NUMKBD + ui_input_scr >= ui_keyboards[ui_input_kbd].num)
                        ui_input_sel = NUMKBD + 36;
                    if((ui_input_sel == NUMKBD + 36 || ui_input_sel == NUMKBD + 37) && ui_keyboards[ui_input_kbd].num <= 36)
                        ui_input_sel = NUMKBD + 38;
                break;
                case SDLK_BACKSPACE:
backsp:             if(ui_input_cur > ui_input_str) {
                        s = ui_input_cur;
                        do { ui_input_cur--; } while(ui_input_cur > ui_input_str && (*ui_input_cur & 0xC0) == 0x80);
                        for(x = 0; s + x <= ui_input_end; x++) ui_input_cur[x] = s[x];
                        ui_input_end -= s - ui_input_cur;
                    }
                break;
                case SDLK_SPACE:
                    if(ui_input_gp && l) {
dosel:                  switch(ui_input_sel) {
                            case NUMKBD + 36: if(ui_input_scr > 0) ui_input_scr -= 36; break;
                            case NUMKBD + 37: if(ui_input_scr + 36 < ui_keyboards[ui_input_kbd].num) ui_input_scr += 36; break;
                            case NUMKBD + 38: ui_input_addkey(" "); break;
                            case NUMKBD + 39: goto backsp;
                            case NUMKBD + 40: ui_input_stop(); return 2;
                            default:
                                if(ui_input_sel < NUMKBD) {
                                    ui_input_kbd = ui_input_sel;
                                    if(ui_input_scr > ui_keyboards[ui_input_kbd].num)
                                        ui_input_scr = 0;
                                } else
                                    ui_input_addkey((char*)ui_keyboards[ui_input_kbd].keys[ui_input_sel - NUMKBD + ui_input_scr]);
                            break;
                        }
                    }
                break;
            }
        break;
        case SDL_CONTROLLERBUTTONDOWN:
            switch(event.cbutton.button) {
                case SDL_CONTROLLER_BUTTON_BACK: strcpy(ui_input_str, ui_input_buf); ui_input_stop(); return 0;
                case SDL_CONTROLLER_BUTTON_START: ui_input_sel = NUMKBD + 40; ui_input_p = 1; break;
                case SDL_CONTROLLER_BUTTON_A:
                case SDL_CONTROLLER_BUTTON_B:
                case SDL_CONTROLLER_BUTTON_X:
                case SDL_CONTROLLER_BUTTON_Y: ui_input_p = 1; break;
                case SDL_CONTROLLER_BUTTON_DPAD_UP: goto up;
                case SDL_CONTROLLER_BUTTON_DPAD_DOWN: goto down;
                case SDL_CONTROLLER_BUTTON_DPAD_LEFT: goto left;
                case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: goto right;
            }
        break;
        case SDL_CONTROLLERBUTTONUP: ui_input_p = 0; goto dosel;
        case SDL_CONTROLLERAXISMOTION:
            if(event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTY || event.caxis.axis == SDL_CONTROLLER_AXIS_RIGHTY) {
                if(event.caxis.value < -main_gptres) goto up;
                if(event.caxis.value > main_gptres) goto down;
            }
            if(event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTX || event.caxis.axis == SDL_CONTROLLER_AXIS_RIGHTX) {
                if(event.caxis.value < -main_gptres) goto left;
                if(event.caxis.value > main_gptres) goto right;
            }
        break;
        case SDL_MOUSEBUTTONDOWN:
            if(event.button.y >= y && event.button.y < y + h && event.button.x >= x && event.button.x < x + w * NUMKBD) {
                ui_input_kbd = ui_input_sel = (event.button.x - x) / w; ui_input_p = 1;
                if(ui_input_scr > ui_keyboards[ui_input_kbd].num)
                    ui_input_scr = 0;
            } else
            if(event.button.y >= y + h && event.button.y < y + 4 * h) {
                x = NUMKBD + (event.button.y - y - h) / h * 12 + event.button.x / w;
                if(x - NUMKBD + ui_input_scr < ui_keyboards[ui_input_kbd].num) {
                    ui_input_sel = x; ui_input_p = 1;
                }
            } else
            if(event.button.y >= y + 4 * h && event.button.y < y + 5 * h) {
                ui_input_p = 1;
                if(event.button.x < w) ui_input_sel = NUMKBD + 36; else
                if(event.button.x < 2 * w) ui_input_sel = NUMKBD + 37; else
                if(event.button.x < 8 * w) ui_input_sel = NUMKBD + 38; else
                if(event.button.x < 10 * w) ui_input_sel = NUMKBD + 39; else ui_input_sel = NUMKBD + 40;
            }
        break;
        case SDL_MOUSEBUTTONUP:
            ui_input_p = 0;
            if(event.button.y >= y + h && event.button.y < y + 4 * h) {
                if((NUMKBD + (event.button.y - y - h) / h * 12 + event.button.x / w) == ui_input_sel) goto dosel;
            } else
            if(event.button.y >= y + 4 * h && event.button.y < y + 5 * h) {
                if(event.button.x < w) x = NUMKBD + 36; else
                if(event.button.x < 2 * w) x = NUMKBD + 37; else
                if(event.button.x < 8 * w) x = NUMKBD + 38; else
                if(event.button.x < 10 * w) x = NUMKBD + 39; else x = NUMKBD + 40;
                if(x != ui_input_sel) ui_input_sel = x; else goto dosel;
            }
        break;
    }
    return 0;
}

/**
 * Text input box view
 */
void ui_input_view()
{
    int i, j, p, x, y, w, h, c;
    uint32_t *data, u;
    SDL_Rect dst;
    char *s = ui_input_str;

    if(inpbg) {
        SDL_LockTexture(inpbg, NULL, (void**)&data, &p);
        for(i = (ui_input_rect.y + ui_input_rect.h + 4) * p / 4; i < win_w * win_h / 2; i++) data[i] = 0xCF000000;
        for(j = 0; j < ui_input_rect.h + 8; j++)
            for(i = 0; i < ui_input_rect.w + 8; i++)
                data[(j + ui_input_rect.y - 4) * win_w + ui_input_rect.x + i - 4] = 0xFF000000;
        ssfn_dst.ptr = (uint8_t*)data;
        ssfn_dst.w = win_w;
        ssfn_dst.h = win_h / 2;
        ssfn_dst.p = p;
        ssfn_dst.x = ui_input_rect.x;
        ssfn_dst.y = ui_input_rect.y;
        ssfn_dst.fg = 0xff808080;
        while(*s && ssfn_dst.x < win_w - 24) { u = ssfn_utf8(&s); ssfn_putc(ui_input_pass == 1 ? '*' : u); }
        if(((main_tick / 333) & 1) && ssfn_dst.x < win_w - 24) ssfn_putc(9474);
        y = ui_input_rect.y + ui_input_rect.h + 8; w = win_w / 12; h = (win_h / 2 - y) / 5; c = (w < h ? w : h) - 4;
        x = (win_w - w * NUMKBD) / 2;
        for(i = 0; i < NUMKBD; i++, x += w)
            ui_keybox(data, p, x + (w - c) / 2, y + (h - c) / 2, c, c, (char*)ui_keyboards[i].id,
                i == ui_input_sel ? 1 : (i == ui_input_kbd ? 2 : 0), i == ui_input_sel ? ui_input_p : 0);
        y += h;
        for(i = j = 0; j < 3; j++, y += h)
            for(x = 0; x < 12 && i + ui_input_scr < ui_keyboards[ui_input_kbd].num; x++, i++)
                ui_keybox(data, p, x * w + (w - c) / 2, y + (h - c) / 2, c, c, (char*)ui_keyboards[ui_input_kbd].keys[i +
                    ui_input_scr], ui_input_sel == NUMKBD + i ? 1 : 0, ui_input_p);
        ui_keybox(data, p, (w - c) / 2, y + (h - c) / 2, c, c, "", ui_input_sel == NUMKBD + 36 ? 1 : 0,
            ui_keyboards[ui_input_kbd].num <= 36 ? -1 : ui_input_p);
        ui_keybox(data, p, w + (w - c) / 2, y + (h - c) / 2, c, c, "", ui_input_sel == NUMKBD + 37 ? 1 : 0,
            ui_keyboards[ui_input_kbd].num <= 36 ? -1 : ui_input_p);
        ui_keybox(data, p, 2 * w + (w - c) / 2, y + (h - c) / 2, c + 5 * w, c, " ", ui_input_sel == NUMKBD + 38 ? 1 : 0,
            ui_input_p);
        ui_keybox(data, p, 8 * w + (w - c) / 2, y + (h - c) / 2, c + w, c, "", ui_input_sel == NUMKBD + 39 ? 1 : 0,
            ui_input_p);
        ui_keybox(data, p, 10 * w + (w - c) / 2, y + (h - c) / 2, c + w, c, "", ui_input_sel == NUMKBD + 40 ? 1 : 0,
            ui_input_p);
        SDL_UnlockTexture(inpbg);
        dst.x = 0; dst.w = win_w; dst.y = dst.h = win_h / 2;
        SDL_RenderCopy(renderer, inpbg, NULL, &dst);
    }
}
