/*
 * tngp/license.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 Get license key from user
 *
 */

#include "tngp.h"
#include "SDL_mixer.h"
#include "mbedtls/common.h"
#include "mbedtls/sha256.h"

char license_key[16];
int license_btn, license_sel, license_cur, license_pressed, license_shake, license_fail;
Mix_Chunk *license_error = NULL;

/**
 * Redraw
 */
void license_redraw()
{
    int i, j, k = (scr_w - license_btn * 10) / 2, l;
    char tmp[8] = { 0 };

    if(screen) {
        SDL_LockTexture(screen, NULL, (void**)&scr_data, &scr_p);
        ssfn_dst.fg = 0xff404040;
        main_text(screen, (win_w - main_textwidth(lang[LICENSE_KEY])) / 2, 0, lang[LICENSE_KEY]);
        ssfn_dst.fg = 0xffffffff;
        ssfn_dst.x = ((win_w - 144) / 2);
        ssfn_dst.y = 26;
        l = 24 * scr_p/4 + ssfn_dst.x - 4;
        for(i = 0; i < 150; i++)
            scr_data[l + i] = i < 38 || (i >= 56 && i < 94) || i >= 112 ? 0xff404040 : 0;
        l += scr_p/4;
        for(j = 0; j < 18; j++) {
            for(i = 0; i < 151; i++)
                scr_data[l + j * scr_p/4 + i] = !i || i == 56 || i == 112 ? 0xff404040 :
                    (i == 38 || i == 94 || i == 150 ? 0xff606060 : ((i > 0 && i < 38) || (i > 56 && i < 94) ||
                    (i > 112 && i < 150) ? 0xff040404 : 0));
        }
        l += 18 * scr_p/4;
        for(i = 1; i < 151; i++)
            scr_data[l + i] = i <= 38 || (i > 56 && i <= 94) || i > 112 ? 0xff606060 : 0;
        for(i = 0; license_key[i]; i++) {
            if(i == 4 || i == 9) ssfn_dst.x += 24;
            else ssfn_putc(license_key[i]);
        }
        ssfn_dst.fg = 0xff606060;
        ssfn_dst.x = win_w / 2 - 32;
        ssfn_putc('-');
        ssfn_dst.x = win_w / 2 + 24;
        ssfn_putc('-');
        for(j = 0; j < 4; j++)
            for(i = 0; i < (j == 3 ? 6 : 10); i++) {
                tmp[0] = !j ? i + '0' : (j - 1) * 10 + i + 'A';
                ui_keybox(scr_data, scr_p, k + i * license_btn, 90 + j * license_btn, license_btn, license_btn,
                    tmp, j * 10 + i == license_sel, license_pressed);
            }
        tmp[0] = 2; tmp[1] = 0;
        ui_keybox(scr_data, scr_p, k + 6 * license_btn, 90 + 3 * license_btn, 2 * license_btn, license_btn, tmp,
            license_sel == 36, license_pressed);
        tmp[0] = 1; tmp[1] = 0;
        ui_keybox(scr_data, scr_p, k + 8 * license_btn, 90 + 3 * license_btn, 2 * license_btn, license_btn, tmp,
            license_sel == 37, license_pressed);
        SDL_UnlockTexture(screen);
    }
}

/**
 * Initialize
 */
void license_init()
{
    mbedtls_sha256_context sha;
    unsigned char hash[32];
    char *keyfile, *s, *d, key[70];
    int i;
    FILE *f;

    memset(license_key, 0, sizeof(license_key));
    license_sel = license_shake = -1; license_cur = license_fail = 0;
    if(!main_fn || !*main_fn) {
        main_switchtab(TAB_LAUNCHER);
        return;
    }
    /* workaround emscipten stupidity */
    if(strcmp(main_fn, "null")) {
        /* try to load the license key */
        keyfile = (char*)main_alloc(strlen(main_fn) + 16);
        strcpy(keyfile, main_fn);
        s = strrchr(keyfile, SEP[0]);
        if(!s) s = keyfile; else s++;
        strcpy(s, "key.txt");
        f = files_open(keyfile, "rb");
        if(f && fread(license_key, 1, 14, f) == 14 && license_key[4] == '-' && license_key[9] == '-' &&
          tng_lickey(license_key, world.hdr.enc)) {
            if(verbose) printf("tngp: license key loaded from '%s'\r\n", keyfile);
            fclose(f);
            free(keyfile);
            main_switchtab(TAB_SPLASH);
            return;
        }
        memset(license_key, 0, sizeof(license_key));
        if(verbose) printf("tngp: unable to load a valid license key from '%s'\r\n", keyfile);
        if(f) fclose(f);
        free(keyfile);
        if(files_config) {
            memset(key, 0, sizeof(key)); key[0] = '_';
            mbedtls_sha256_init(&sha);
            mbedtls_sha256_starts_ret(&sha, 0);
            mbedtls_sha256_update_ret(&sha, (uint8_t*)main_fn, strlen(main_fn));
            mbedtls_sha256_finish_ret(&sha, hash);
            mbedtls_sha256_free(&sha);
            for(i = 0, s = key + 1; i < 32; i++) s += sprintf(s, "%02x", hash[i]);
            keyfile = (char*)main_alloc(strlen(files_config) + 16);
            strcpy(keyfile, files_config);
            strcat(keyfile, SEP "licenses.json");
            s = files_readfile(keyfile, NULL);
            if(s) {
                d = json_get(s, key); if(d) { strcpy(license_key, d); free(d); }
                free(s);
            }
            free(keyfile);
            if(license_key[13] && license_key[4] == '-' && license_key[9] == '-' && tng_lickey(license_key, world.hdr.enc)) {
                if(verbose) printf("tngp: license key found in database\r\n");
                main_switchtab(TAB_SPLASH);
                return;
            }
        }
    }
    scr_w = win_w; scr_h = win_h / 2 + 16 + 24;
    license_btn = (scr_h - 96) / 4;
    if(license_btn > scr_w / 10) license_btn = scr_w / 10;
    screen = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, scr_w, scr_h);
    license_redraw();
    audio = Mix_OpenAudio(44100, AUDIO_S16LSB, 2, 4096) >= 0;
    if(audio) {
        license_error = Mix_LoadWAV_RW(SDL_RWFromConstMem(binary_err_ogg, sizeof(binary_err_ogg)), 1);
        if(license_error) Mix_VolumeChunk(license_error, MIX_MAX_VOLUME);
    }
    SDL_ShowCursor(SDL_ENABLE);
}

/**
 * Save license to key.txt or local database
 */
void license_save()
{
    mbedtls_sha256_context sha;
    unsigned char hash[32];
    char *keyfile, *s, *e, key[65];
    int i;
    FILE *f;

    if((main_fn && !strcmp(main_fn, "null")) || !license_key[0]) return;

    if(main_fn && *main_fn) {
        /* first, try to save the license key next to the game file */
        keyfile = (char*)main_alloc(strlen(main_fn) + 16);
        strcpy(keyfile, main_fn);
        s = strrchr(keyfile, SEP[0]);
        if(!s) s = keyfile; else s++;
        strcpy(s, "key.txt");
        f = files_open(keyfile, "wb");
        if(f) {
            i = fwrite(license_key, 1, 14, f);
            fclose(f);
            if(i == 14) {
                if(verbose) printf("tngp: license key saved\r\n");
                free(keyfile);
                return;
            }
        }
        free(keyfile);
    }
    if(files_config) {
        /* use global license db */
        memset(key, 0, sizeof(key));
        mbedtls_sha256_init(&sha);
        mbedtls_sha256_starts_ret(&sha, 0);
        mbedtls_sha256_update_ret(&sha, (uint8_t*)main_fn, strlen(main_fn));
        mbedtls_sha256_finish_ret(&sha, hash);
        mbedtls_sha256_free(&sha);
        for(i = 0, s = key; i < 32; i++) s += sprintf(s, "%02x", hash[i]);
        keyfile = (char*)main_alloc(strlen(files_config) + 16);
        strcpy(keyfile, files_config);
        strcat(keyfile, SEP "licenses.json");
        s = files_readfile(keyfile, NULL);
        f = files_open(keyfile, "wb");
        if(f) {
            if(s) {
                for(e = s + strlen(s) - 1; e > s + 1 && e[-1] != '\"'; e--);
                if(e > s + 1 && e[-1] == '\"') {
                    fwrite(s, 1, e - s, f);
                    fprintf(f, ",\r\n");
                } else
                    fprintf(f, "{\r\n");
            } else
                fprintf(f, "{\r\n");
            fprintf(f, "\"_%s\": \"%s\"\r\n}", key, license_key);
            fclose(f);
            if(verbose) printf("tngp: license key added to database\r\n");
        }
        if(s) free(s);
        free(keyfile);
    }
}

/**
 * Free resources
 */
void license_free()
{
    if(screen) { SDL_DestroyTexture(screen); screen = NULL; }
    if(license_error) { Mix_FreeChunk(license_error); license_error = NULL; }
    if(audio) { Mix_CloseAudio(); audio = 0; }
    SDL_ShowCursor(SDL_DISABLE);
}

/**
 * Controller
 */
int license_ctrl()
{
    int i, j;

    switch(event.type) {
        case SDL_KEYDOWN:
            switch(event.key.keysym.sym) {
                case SDLK_AC_BACK:
                case SDLK_F10:
                case SDLK_ESCAPE: return 0;
                case SDLK_UP:
up:                 if(license_sel == 37) license_sel = 28; else
                    if(license_sel > 10) license_sel -= 10; else license_sel = 0;
                break;
                case SDLK_DOWN:
down:               if(license_sel == 27) license_sel = 36; else
                    if(license_sel < 28) license_sel += 10; else license_sel = 37;
                break;
                case SDLK_LEFT:
left:               if(license_sel > 0) license_sel--; else license_sel = 0;
                break;
                case SDLK_RIGHT:
right:              if(license_sel < 37) license_sel++; else license_sel = 37;
                break;
                case SDLK_SPACE: license_pressed = 1; break;
                case SDLK_BACKSPACE: license_sel = 36; license_pressed = 1; break;
                case SDLK_RETURN: license_sel = 37; license_pressed = 1; break;
                default:
                    if(event.key.keysym.sym >= SDLK_0 && event.key.keysym.sym <= SDLK_9) {
                        license_sel = event.key.keysym.sym - SDLK_0; license_pressed = 1;
                    } else
                    if(event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z) {
                        license_sel = event.key.keysym.sym - SDLK_a + 10; license_pressed = 1;
                    }
                break;
            }
            license_redraw();
        break;
        case SDL_KEYUP:
keypress:   if(license_pressed) {
                if(license_sel == 37) {
                    if(license_key[13] && license_fail < 3 && tng_lickey(license_key, world.hdr.enc)) {
                        license_save();
                        main_switchtab(TAB_LOADING);
                        return 1;
                    }
                    license_sel = -1; license_shake = 0; license_fail++;
                    if(audio && license_error)
                        Mix_PlayChannel(0, license_error, 0);
                } else
                if(license_sel == 36) {
                    if(license_cur > 0) {
                        license_cur--; if(license_cur == 4 || license_cur == 9) license_cur--;
                        license_key[license_cur] = 0;
                    }
                } else
                if(license_sel >= 0 && license_cur < 14) {
                    license_key[license_cur++] = license_sel < 10 ? license_sel + '0' : license_sel - 10 + 'A';
                    if(license_cur == 4 || license_cur == 9) license_key[license_cur++] = '-';
                }
                license_pressed = 0;
            }
            if(event.type == SDL_MOUSEBUTTONUP || event.key.keysym.sym == SDLK_BACKSPACE ||
              (event.key.keysym.sym >= SDLK_0 && event.key.keysym.sym <= SDLK_9) ||
              (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)) license_sel = -1;
            license_redraw();
        break;
        case SDL_MOUSEBUTTONDOWN:
            j = win_h / 2 - 16 - 24 + 90; i = win_w / 2 - 5 * license_btn;
            if(event.button.y >= j && event.button.y < win_h - 6 && event.button.x >= i && event.button.x < i + 10 * license_btn) {
                i = (event.button.x - i) / license_btn + (event.button.y - j) / license_btn * 10;
                if(i == 37) license_sel = 36; else
                if(i >= 38) license_sel = 37; else license_sel = i;
                license_pressed = 1;
                license_redraw();
            }
        break;
        case SDL_MOUSEBUTTONUP:
        case SDL_CONTROLLERBUTTONUP: goto keypress;
        case SDL_CONTROLLERBUTTONDOWN:
            switch(event.cbutton.button) {
                case SDL_CONTROLLER_BUTTON_BACK: return 0;
                case SDL_CONTROLLER_BUTTON_START: license_sel = 37; license_pressed = 1; break;
                case SDL_CONTROLLER_BUTTON_A:
                case SDL_CONTROLLER_BUTTON_B:
                case SDL_CONTROLLER_BUTTON_X:
                case SDL_CONTROLLER_BUTTON_Y: license_pressed = 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;
            }
            license_redraw();
        break;
        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;
    }
    return 1;
}

/**
 * View
 */
void license_view()
{
    int shake[] = { 2, 8, 10, 6, -2, -8, -10, -6, 2, 0 };
    SDL_Rect rect;

    if(!main_draw) return;
    if(tab == TAB_SPLASH) { main_splash(); return; }
    if(tab == TAB_LAUNCHER) { launcher_view(); return; }
    rect.x = (win_w - 440) / 2; rect.y = 16; rect.w = lgopos.w; rect.h = lgopos.h;
    SDL_RenderCopy(renderer, logo, &lgopos, &rect);
    if(license_shake != -1) {
        rect.x = shake[license_shake++];
        if(!rect.x) {
            license_shake = -1; license_cur = 0;
            memset(license_key, 0, sizeof(license_key));
            license_redraw();
        }
    } else
        rect.x = 0;
    rect.w = win_w;
    rect.y = win_h / 2 - 16 - 24; rect.h = win_h / 2 + 16 + 24;
    if(screen) SDL_RenderCopy(renderer, screen, NULL, &rect);
}
