/*
 * Copyright (C) 2023 Daniele Lacamera <root@danielinux.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include "stm32f7_ltdc.h"
#include "ui.h"
#include "systick.h"
#include "button.h"


static uint8_t menu_on = 0;

uint8_t ui_menu_is_on(void)
{
    return menu_on;
}

void ui_menu(uint8_t onoff)
{
    menu_on = onoff;
}

#define BUTTON_RIGHT 1
#define BUTTON_LEFT  2
#define BUTTON_UP    0
#define BUTTON_DOWN  3
#define BUTTON_IN    7
#define BUTTON_OUT   6
#define BUTTON_FIRE  5

#define NO_CHANNEL (-1)

enum control_mode_e {
    CTL_RUN,
    CTL_STOP,
    CTL_PAUSE,
    CTL_TRIGGER,
    CTL_MAX
};

int res[] = {
   1000000 , 500000 , 200000 , 100000 , 50000 , 20000 , 10000 , 5000 , 2000 , 1000 , 500 , 200 , 100 , 50 , 20 , 0
};

#define FONT_HEIGHT 8
#define FONT_WIDTH  8
extern const unsigned char fb_font[256][FONT_WIDTH];

uint8_t *ui_get_screen(int layer)
{
    if (layer == 0)
        return (uint8_t *)Screen_address;
    else
        return (uint8_t *)Overlay_address;

}

void ui_draw_h_segment(int layer, uint8_t color, uint32_t row, uint32_t start, uint32_t len)
{
    uint32_t i;
    uint32_t pos = row * xres + start;
    uint8_t *screen = ui_get_screen(layer);
    for (i = 0; i < len; i++) {
        screen[pos + i] = color;
    }
}

void ui_draw_v_segment(int layer, uint8_t color, uint32_t col, uint32_t start, uint32_t len)
{
    uint32_t i;
    uint32_t pos = start * xres + col;
    uint8_t *screen = ui_get_screen(layer);
    for (i = 0; i < len; i++) {
        screen[pos + i * xres] = color;
    }
}

void ui_fill_area(int layer, uint8_t color, uint32_t x, uint32_t y, uint32_t x1, uint32_t y1)
{
    int i;
    for (i = y; i < y1; i++) {
        ui_draw_h_segment(layer, color, i, x, x1 - x);
    }
}

void ui_text_at(int layer, uint8_t color, uint32_t x, uint32_t y, char *text)
{
    uint32_t i, j;
    char fc;
    const uint8_t *render;
    uint32_t pos = y * xres + x;
    uint8_t *screen = ui_get_screen(layer);

    while(text[0]) {
        uint32_t cur = pos;
        fc = text[0];
        render = fb_font[(int)fc];
        for (i = 0; i < FONT_HEIGHT; i++) {
            for (j = 0; j < FONT_WIDTH; j++) {
                int right_shift = (FONT_HEIGHT - 1) - j;
                cur = pos + i * xres + j;
                if (render[i] & (1 << (7 - j))) {
                    screen[cur] = color;
                }
            }
        }
        pos += FONT_WIDTH;
        text++;
    }
}

void ui_selected_text_at(int layer, uint8_t color, uint8_t highlight, uint32_t x, uint32_t y, char *text)
{
    ui_fill_area(layer, highlight, x, y, x + (FONT_WIDTH * strlen(text)), y + FONT_HEIGHT);
    ui_text_at(layer, color, x, y, text);
}

void image_at(int layer, const uint8_t *img, uint32_t x, uint32_t y, uint32_t size_x, uint32_t size_y)
{
    uint32_t i, j;
    uint32_t pos = y * xres + x;
    uint8_t *screen = ui_get_screen(layer);

    for (i = 0; i < size_y; i++) {
        for (j = 0; j < size_x; j++) {
            screen[pos + i * xres + j] = img[i * size_x + j];
        }
    }
}
 /**** ****/


static char _ui_printn_res[20];
char *ui_printn(uint32_t n)
{
    int i = 0;
    uint32_t div = 1000000000;
    int started = 0;


    while (div >= 10) {
        if ((n >= div) || started) {
            _ui_printn_res[i++] = '0' + n / div;
            n = n % div;
            started++;
        }
        div /= 10;
    }
    _ui_printn_res[i++] = '0' + n;
    _ui_printn_res[i] = '\0';
    return (char *)(&_ui_printn_res[0]);
}

char *ui_printn2(uint32_t n)
{
    _ui_printn_res[0] = '`';
    _ui_printn_res[1] = '0' + (n % 10);
    if (n > 9)
        _ui_printn_res[0] = '0' + (n / 10);
    _ui_printn_res[2] = 0;
    return _ui_printn_res;
}


char *ui_printhex(uint8_t n)
{
    uint8_t a = (n & 0xF0) >> 4;
    uint8_t b = (n & 0x0F);
    if (a < 10)
        _ui_printn_res[0] = a + '0';
    else
        _ui_printn_res[0] = (a - 0xa) + 'A';
    if (b < 10)
        _ui_printn_res[1] = b + '0';
    else
        _ui_printn_res[1] = (b - 0xa) + 'A';
    _ui_printn_res[2] = '\0';
    return _ui_printn_res;
}

char *ui_printhex1(uint8_t n)
{
    uint8_t a = (n & 0x0F);
    if (a < 10)
        _ui_printn_res[0] = a + '0';
    else
        _ui_printn_res[0] = (a - 0xa) + 'A';
    _ui_printn_res[1] = '\0';
    return _ui_printn_res;
}

static void ui_switch_screen(int newscreen);

static void force_redraw(void)
{
    struct screen *s = screen_get_focus();
    if (s && s->draw)
        s->draw();
}

int ui_process_button_pressed(void)
{
    int i;
    for (i = 0; i < N_BUTTONS; i++) {
        if (Input_status.b[i].pressed == 1) {
            Input_status.b[i].pressed = 0;
            return i;
        }
    }
    return -1;
}

void ui_draw_blocks(void)
{
    ui_fill_area(0, BLACK, 420, 90, 440, 120);
    ui_fill_area(0, DARKRED, 420, 100, 440, 110);
}




struct point {
    int x, y;
};

const struct point pot[] = {
    {-9, 17}, {-10, 17}, {-11, 16}, {-12, 16}, {-13, 15}, {-14, 14}, {-15, 13}, {-16, 12}, {-17, 10}, {-18, 8}, {-19, 6}, {-20, 0},
    {-19, -6}, {-18, -8}, {-16, -12}, {-15, -13}, {-14, -14}, {-13, -15}, {-12, -16}, {-11, -16}, {-10, -17}, {-9, -17},  {-8, -18}, {-7, -18}, {-6, -19}, {-5, -19}, {-4, -19}, {-3, -19}, {-2, -19}, {-1, -19}, {0, -20},
    {1, -19}, {2, -19}, {3, -19}, {4, -19}, {5, -19}, {6, -19}, {7, -18}, {8, -18}, {9, -17}, {10, -17}, {11, -16}, {12, -16}, {13, -15}, {14, -14}, {15, -13}, {16, -12}, {17, -10}, {18, -8}, {19, -6}, {20, 0}, 
    {19, 6},  {18, 8},  {17, 10}, {16, 12}, {15, 13}, {14, 14}, {13, 15}, {12, 16}, {11, 16}, {10, 17}, {9, 17},  
};

static void ui_run(uint16_t event)
{
    if (event & EV_BUTTON) {
        int button = ui_process_button_pressed();
        switch(button) {
            case 7:
                if (menu_on) {
                    menu_on = 0;
                    force_redraw();
                } else {
                    menu_on = 1;
                    ui_redraw(EV_NONE);
                }
                break;
        }
        clear_event(EV_BUTTON);
    }
}


static struct screen *ui_get_screen_by_id(int id)
{
    struct screen *s = screens();
    while(s) {
        if (id == s->id) {
            return s;
        }
        s = s->next;
     }
    return NULL;
}

void ui_switch_screen(int newscreen)
{
    struct screen *s = ui_get_screen_by_id(newscreen);
    if (s)
        screen_set_focus(s);
}

void ui_menu_redraw(uint16_t event)
{
    static int selected;
    int button;
    int i;
    int n;
    n = screen_n_screens();

    if (event & EV_BUTTON) {
        button = ui_process_button_pressed();
        switch(button) {
            case 1:
                selected--;
                if (selected < 0)
                    selected = n - 1;
                ui_redraw(EV_NONE);
                break;
            case 3:
                selected++;
                if (selected > n - 1)
                    selected = 0;
                ui_redraw(EV_NONE);
                break;
            case 7:
                menu_on = 0;
                force_redraw();
                break;
            default:
                menu_on = 0;
                ui_switch_screen(selected);
        }
        return;
    }
    ui_fill_area(0, BLACK, 400, 200, 480, 240);
    for (i = 0; i < n; i++) {
        if (i == selected) {
            ui_fill_area(0, WHITE, 400, 200 + i * 10, 480, 210 + i * 10);
            ui_text_at(0, PURPLE, 400, 200 + i*10, ui_get_screen_by_id(i)->name);
        } else {
            ui_text_at(0, WHITE, 400, 200 + i*10, ui_get_screen_by_id(i)->name);
        }
    }
}

void ui_redraw(uint16_t event)
{
    struct screen *sc;
    if (menu_on) {
        ui_menu_redraw(event);
    } else {
        sc = screen_get_focus();
        if (sc && sc->draw)
            sc->draw();
    }
}

void ui_init(void)
{
    int i;
    force_redraw();
}

struct task task_ui = {
    .init = ui_init,
    .run = ui_run,
    .events = EV_BUTTON,
    .name = "ui"
};

void ui_setup(void)
{
    register_task(&task_ui);
}

