/**
 * @file modbus_tcp.c
 *
 * @brief TCP Modbus handler
 * @date Nov 6, 2023
 * @author Obe
 */

// Includes
#include "modbus_tcp.h"
#include "log.h"

// Defines
#define MAX_REG          250
#define EXTENSION_LENGTH 4
#define TEXT_LENGTH      200
#define MULTIPLE_REG     0x10
#define REG_LENGTH       428
#define START_DATA       28
#define MODBUS_MODE      7
#define REG_01           14
#define REG_02           16
#define REG_03           18
#define REG_04           20
#define REG_05           22
#define REG_06           24
#define REG_07           26

// Global variables
char registers[MAX_REG];
// const char* TAG = "Modbus_TCP"; // Tag used in logs

// Functions
static err_t modbus_incoming_data(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err);
static err_t modbus_accept(void* arg, struct tcp_pcb* pcb, err_t err);

/**
 * @fn static err_t modbus_incoming_data(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
 * @brief Function that's called when there is a new request on port 502.
 * It handles the incoming data from Qmodmaster
 */
static err_t modbus_incoming_data(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err) {
    uint8_t counter;
    char* pc;
    char text[TEXT_LENGTH];
    uint8_t background_red = 0;
    uint8_t background_green = 0;
    uint8_t background_blue = 0;
    uint8_t text_color_red = 0;
    uint8_t text_color_green = 0;
    uint8_t text_color_blue = 0;
    uint8_t nr_img = 0;
    uint32_t result_background = 0;
    uint32_t text_foreground_color = 0;

    memset(text, '_', TEXT_LENGTH); // Putting underscores in the whole array
    text[TEXT_LENGTH - 1] = '\0';

    if (p != NULL) {
        // LOG_INFO(TAG, "data not null\n");
        // Process the modbus data
        tcp_recved(pcb, p->tot_len);
        pc = (char*)p->payload;

        for (uint16_t i = 0; i < MAX_REG; i++) { // Putting the buffer in the register array
            registers[i] = pc[i]; // Getting the error "void value not ignored as it ought to be" on this line
        }

        if (registers[MODBUS_MODE] == MULTIPLE_REG) { // Check if it's a Modbus Write Multiple Registers request (0x10)
            // LOG_INFO(TAG, "in writing multiple register mode\n");
            background_red = (uint8_t)(registers[REG_01]);   // 01
            background_green = (uint8_t)(registers[REG_02]); // 02
            background_blue = (uint8_t)(registers[REG_03]);  // 03
            text_color_red = (uint8_t)(registers[REG_04]);   // 04
            text_color_green = (uint8_t)(registers[REG_05]); // 05
            text_color_blue = (uint8_t)(registers[REG_06]);  // 06
            nr_img = (uint8_t)(registers[REG_07]);           // 07

            // LOG_INFO(TAG, "%d %d %d %d %d %d %d
            // ",background_red,background_green,background_blue,text_color_red,text_color_green,text_color_blue,nr_img);

            counter = 0;
            for (int i = START_DATA; i < REG_LENGTH; i++) {
                if (i % 2 == 0) {
                    text[counter] = registers[i];
                    counter++;
                }
            }

            result_background = 0xff000000;
            result_background |= ((uint32_t)background_red) << 16;
            result_background |= ((uint32_t)background_green) << 8;
            result_background |= background_blue;

            text_foreground_color = 0xff000000;
            text_foreground_color |= ((uint32_t)text_color_red) << 16;
            text_foreground_color |= ((uint32_t)text_color_green) << 8;
            text_foreground_color |= text_color_blue;
            // Processing the image index
            size_t number_of_files = llfs_file_count(); // How many files that there are

            llfs_file_t file_list[number_of_files]; // Reserving memory for the list

            number_of_files = llfs_file_list(file_list, number_of_files, NULL); // Freed memory filled with the list

            if (number_of_files < nr_img) {
                lcd_clear_text();
                lcd_clear_images();
                lcd_stop_all_gifs();

                lcd_display_text(text, 10, 10, text_foreground_color, result_background, LCD_FONT24); // When no image

                lcd_display_text("FILE NOT IN FILESYSTEM", 10, 75, LCD_RED, LCD_BLACK, LCD_FONT24);
            } else {
                const char* ext = strrchr(file_list[nr_img - 1].name, '.');
                if (ext == NULL) {
                    // No valid extension found
                }
                if (strcmp(ext, ".gif") == 0) {
                    lcd_clear_text();
                    lcd_clear_images();
                    lcd_stop_all_gifs();

                    lcd_display_text(text, 10, 10, text_foreground_color, result_background, LCD_FONT24);
                    lcd_gif_t* gif = lcd_draw_gif_from_fs(file_list[nr_img - 1].name, 0, 75); // GIF on screen
                } else if (strcmp(ext, ".bmp") == 0) {
                    lcd_clear_text();
                    lcd_clear_images();
                    lcd_stop_all_gifs();
                    lcd_display_text(text, 10, 10, text_foreground_color, result_background, LCD_FONT24);
                    lcd_draw_img_from_fs(file_list[nr_img - 1].name, 0, 75); // BMP on screen
                }
            }
        } else {
            // LOG_INFO(TAG, "not in writing multiple register mode!!!\n");
        }
    } else if (err == ERR_OK) {
        tcp_close(pcb); // When everything was ok close the TCP connection
    }
    return ERR_OK;
}

/**
 * @fn static err_t modbus_accept(void *arg, struct tcp_pcb *pcb, err_t err)
 * @brief Sets the function that's being called when theirs incoming data
 */
static err_t modbus_accept(void* arg, struct tcp_pcb* pcb, err_t err) {
    LWIP_UNUSED_ARG(arg);                // Eliminates compiler warning about unused arguments
    LWIP_UNUSED_ARG(err);                // Eliminates compiler warning about unused arguments
    tcp_setprio(pcb, TCP_PRIO_MIN);      // Sets the priority of a connection.
    tcp_recv(pcb, modbus_incoming_data); // Sets which function is being called when new data arrives

    return ERR_OK;
}

/**
 * @fn void modbus_init
 * @brief Initializes the modbus tcp
 */
void modbus_init(void) {
    struct tcp_pcb* modbus_pcb;
    modbus_pcb = tcp_new();                        // Creating a new tcp pcb
    tcp_bind(modbus_pcb, IP_ADDR_ANY, MODBUSPORT); // Bind the modbus_pcb to port 502

    modbus_pcb = tcp_listen(modbus_pcb);   // Listen
    tcp_accept(modbus_pcb, modbus_accept); // Set callback function
}
