#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"

#include "EVE81X.h"

// Definieren Sie den HSPI-Host
#define HSPI_HOST SPI2_HOST

// Definieren Sie die Anzahl der parallelen Linien
#define PARALLEL_LINES 16

// SPI-Bus-Konfiguration
spi_bus_config_t buscfg = {
    .miso_io_num = 11,
    .mosi_io_num = 13,
    .sclk_io_num = 12,
    .quadwp_io_num = -1,
    .quadhd_io_num = -1,
    .max_transfer_sz = PARALLEL_LINES * 320 * 2 + 8
};

// SPI-Geräte-Konfiguration
spi_device_interface_config_t devcfg = {
    .clock_speed_hz = 1 * 1000 * 1000,  // SPI-Taktfrequenz (1 MHz)
    .mode = 0,  // SPI-Modus 0
    .spics_io_num = 10,  // CS (Chip-Select) Pin
    .queue_size = 7,  // Warteschlangengröße
};




// Funktion zur Lese des Chip-Identifikationscodes
uint32_t readChipID(spi_device_handle_t spi) {
    uint8_t tx_data[4] = {0x00, 0x00, 0x00, 0x00};  // Befehl zum Lesen der Chip-ID
    uint8_t rx_data[4];

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));

    t.flags = 0;
    t.length = 32;  // 32 Bit (4 Bytes)
    t.tx_buffer = tx_data;
    t.rxlength = 32;
    t.rx_buffer = rx_data;

    t.addr = ID_READ_ADDRESS;  // Adresse zum Lesen der Chip-ID
    t.cmd = 0;  // Kein zusätzlicher Befehl erforderlich

    esp_err_t ret = spi_device_transmit(spi, &t);
    assert(ret == ESP_OK);

    // Interpretiere die gelesenen Daten entsprechend der Tabelle
    uint32_t chipID = ((uint32_t)rx_data[3] << 24) |
                     ((uint32_t)rx_data[2] << 16) |
                     ((uint32_t)rx_data[1] << 8) |
                     rx_data[0];

    return chipID;
}


// Funktion zum Senden von EVE-Host-Befehlen
void host_command(spi_device_handle_t spi, uint8_t cmd) {
    uint8_t tx_data = cmd;
    uint8_t rx_data;

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));

    t.flags = 0;
    t.length = 8;
    t.tx_buffer = &tx_data;
    t.rxlength = 0;
    t.rx_buffer = &rx_data;

    esp_err_t ret = spi_device_transmit(spi, &t);
    assert(ret == ESP_OK);
}

// Funktion zum Schreiben von 32-Bit-Werten in EVE-Register
void write_eve_register_32(spi_device_handle_t spi, uint32_t addr, uint32_t value) {
    uint8_t tx_data[8];
    memset(tx_data, 0, sizeof(tx_data));

    tx_data[0] = (addr >> 16) & 0x3F;
    tx_data[1] = (addr >> 8) & 0xFF;
    tx_data[2] = addr & 0xFF;
    tx_data[4] = (value >> 24) & 0xFF;
    tx_data[5] = (value >> 16) & 0xFF;
    tx_data[6] = (value >> 8) & 0xFF;
    tx_data[7] = value & 0xFF;

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));

    t.flags = 0;
    t.length = 64;
    t.tx_buffer = tx_data;
    t.rxlength = 0;
    t.rx_buffer = NULL;

    esp_err_t ret = spi_device_transmit(spi, &t);
    assert(ret == ESP_OK);
}


uint16_t rd16(spi_device_handle_t spi, uint32_t address) {
    uint8_t tx_data[4];
    uint8_t rx_data[2];

    // Konfiguration des Transaktionsobjekts
    spi_transaction_t t;
    memset(&t, 0, sizeof(t));
    t.flags = 0;
    t.length = 32;  // 32 Bit (4 Bytes)
    t.tx_buffer = tx_data;
    t.rxlength = 16;  // 16 Bit (2 Bytes)
    t.rx_buffer = rx_data;
    t.addr = address;  // Adresse zum Lesen der 16-Bit-Daten
    t.cmd = 0;  // Kein zusätzlicher Befehl erforderlich

    esp_err_t ret = spi_device_transmit(spi, &t);
    assert(ret == ESP_OK);

    // Die gelesenen Daten zusammensetzen
    uint16_t data = ((uint16_t)rx_data[1] << 8) | rx_data[0];

    return data;
}

void wr32(uint32_t registerAddress, uint32_t value, spi_device_handle_t spi) {
    uint32_t combinedValue = (registerAddress << 16) | (value & 0xFFFF);

    uint8_t tx_data[8];
    memset(tx_data, 0, sizeof(tx_data));

    tx_data[0] = (combinedValue >> 16) & 0x3F;
    tx_data[1] = (combinedValue >> 8) & 0xFF;
    tx_data[2] = combinedValue & 0xFF;

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));

    t.flags = 0;
    t.length = 24;  // 24 bits for the combined 32-bit value
    t.tx_buffer = tx_data;
    t.rxlength = 0;
    t.rx_buffer = NULL;

    esp_err_t ret = spi_device_transmit(spi, &t);
    assert(ret == ESP_OK);
}


void wr16(uint32_t registerAddress, uint16_t value, spi_device_handle_t spi) {
    uint32_t combinedValue = (registerAddress << 16) | value;

    uint8_t tx_data[8];
    memset(tx_data, 0, sizeof(tx_data));

    tx_data[0] = (combinedValue >> 16) & 0x3F;
    tx_data[1] = (combinedValue >> 8) & 0xFF;
    tx_data[2] = combinedValue & 0xFF;

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));

    t.flags = 0;
    t.length = 24;  // 24 bits for the combined 32-bit value
    t.tx_buffer = tx_data;
    t.rxlength = 0;
    t.rx_buffer = NULL;

    esp_err_t ret = spi_device_transmit(spi, &t);
    assert(ret == ESP_OK);
}

void wr8(uint32_t registerAddress, uint8_t value, spi_device_handle_t spi) {
    uint32_t combinedValue = (registerAddress << 8) | value;

    uint8_t tx_data[8];
    memset(tx_data, 0, sizeof(tx_data));

    tx_data[0] = (combinedValue >> 16) & 0x3F;
    tx_data[1] = (combinedValue >> 8) & 0xFF;
    tx_data[2] = combinedValue & 0xFF;

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));

    t.flags = 0;
    t.length = 24;  // 24 bits for the combined 32-bit value
    t.tx_buffer = tx_data;
    t.rxlength = 0;
    t.rx_buffer = NULL;

    esp_err_t ret = spi_device_transmit(spi, &t);
    assert(ret == ESP_OK);
}

void dl(uint32_t command, spi_device_handle_t spi) {
    // Display List Adresse (z.B., Adresse im RAM_DL)
    uint32_t dlAddress = RAM_DL; 

    // Kombiniere Befehl mit der Display List Adresse
    uint32_t combinedCommand = (dlAddress << 16) | (command & 0xFFFF);

    // Vorbereite die Daten für die Übertragung
    uint8_t tx_data[8];
    memset(tx_data, 0, sizeof(tx_data));

    tx_data[0] = (combinedCommand >> 16) & 0x3F;
    tx_data[1] = (combinedCommand >> 8) & 0xFF;
    tx_data[2] = combinedCommand & 0xFF;

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));

    t.flags = 0;
    t.length = 24;  // 24 bits for the combined 32-bit value
    t.tx_buffer = tx_data;
    t.rxlength = 0;
    t.rx_buffer = NULL;

    // Übertrage die Daten über SPI und schreibe sie in RAM_DL
    esp_err_t ret = spi_device_transmit(spi, &t);
    assert(ret == ESP_OK);
}


void app_main() {
    esp_err_t ret;

    ret = spi_bus_initialize(HSPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
    if (ret != ESP_OK) {
        printf("Fehler bei der Initialisierung des SPI-Busses: %s (Fehlercode: 0x%x)\n", esp_err_to_name(ret), ret);
    }

    spi_device_handle_t spi;
    ret = spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
    if (ret != ESP_OK) {
        printf("Fehler beim Hinzufügen der SPI Geräte: %s\n", esp_err_to_name(ret));
    }

    uint32_t chipID = readChipID(spi);
    printf("Chip ID: 0x%08lx\n", (unsigned long)chipID);

    host_command(spi, HCMD_CLKEXT);  // Host-Befehl "CLKEXT"
    host_command(spi, CMD_CLKSEL);  // Host-Befehl "CLKSEL"
    host_command(spi, HCMD_ACTIVE);  // Host-Befehl "ACTIVE"
    host_command(spi, CMD_RST_PULSE);  // Host-Befehl "RST_PULSE"
    while (0x0 != rd16(spi, REG_CPURESET));  //Check if EVE is in working status.

    wr32(REG_FREQUENCY, 0x3938700, spi);

    wr16(REG_HCYCLE, 928, spi);
    wr16(REG_HOFFSET, 88, spi);
    wr16(REG_HSYNC0,   0, spi);
    wr16(REG_HSYNC1,  48, spi);
    wr16(REG_VCYCLE, 525, spi);
    wr16(REG_VOFFSET, 32, spi);
    wr16(REG_VSYNC0,   0, spi);
    wr16(REG_VSYNC1,   3, spi);
    wr8(REG_SWIZZLE,   0, spi);
    wr8(REG_PCLK_POL,  1, spi);
    wr8(REG_CSPREAD,   0, spi);
    wr16(REG_HSIZE,  800, spi);
    wr16(REG_VSIZE,  480, spi);

    /* Write first display list to display list memory RAM_DL*/
    wr32(RAM_DL+0,CLEAR_COLOR_RGB(0,0,0), spi);
    wr32(RAM_DL+4,CLEAR(1,1,1), spi);
    wr32(RAM_DL+8,DISPLAY(), spi);
    wr8(REG_DLSWAP,0x02, spi);//display list swap

    wr8(REG_DLSWAP,0x02, spi);//display list swap
    /* Enable backlight of display panel */
    wr16(REG_GPIOX_DIR, 0xffff, spi);
    wr16(REG_GPIOX, 0xffff, spi);

    wr8(REG_PCLK,2, spi);    

    wr32(RAM_DL + 0, CLEAR(1, 1, 1), spi); // clear screen
    wr32(RAM_DL + 4, BEGIN(BITMAPS), spi); // start drawing bitmaps
    wr32(RAM_DL + 8,  VERTEX2II(220, 110, 31, 'T'), spi); // ASCII T in font 31
    wr32(RAM_DL + 12, VERTEX2II(244, 110, 31, 'E'), spi); // ASCII E in font 31
    wr32(RAM_DL + 16, VERTEX2II(270, 110, 31, 'X'), spi); // ASCII X in font 31
    wr32(RAM_DL + 20, VERTEX2II(299, 110, 31, 'T'), spi); // ASCII T in font 31
    wr32(RAM_DL + 24, END(), spi);
    wr32(RAM_DL + 28, COLOR_RGB(160, 22, 22), spi); // change colour to red
    wr32(RAM_DL + 32, POINT_SIZE(320), spi); // set point size to 20 pixels in radius
    wr32(RAM_DL + 36, BEGIN(POINTS), spi); // start drawing points
    wr32(RAM_DL + 40, VERTEX2II(192, 133, 0, 0), spi); // red point
    wr32(RAM_DL + 44, END(), spi);
    wr32(RAM_DL + 48, DISPLAY(), spi); // display the image

    spi_bus_remove_device(spi);
    spi_bus_free(HSPI_HOST);
}
