/*
 * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */

#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <ctype.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

#include "driver/gpio.h"
#include "driver/spi_common.h"
#include "driver/spi_master.h"

#include "driver/i2c_master.h"
#include "driver/i2c_types.h"


static const char *TAG = "TEST";

#define I2C_BUS_PORT  0

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define I2C_PIXEL_CLOCK_HZ          (400 * 1000)
#define I2C_PIN_NUM_SDA             GPIO_NUM_17
#define I2C_PIN_NUM_SCL             GPIO_NUM_18
#define I2C_PIN_NUM_RST             GPIO_NUM_21
#define I2C_HW_ADDR                 0x3C

// The pixel number in horizontal and vertical
#define LCD_H_RES                   128
#define LCD_V_RES                   64

//******************************************************************************
// LED GPIO interface to ESP32-S3
//******************************************************************************

#define GPIO_LED                    GPIO_NUM_35


//------------------------------------------------------------------------------
// Data
//------------------------------------------------------------------------------

// To use LV_COLOR_FORMAT_I1, we need an extra buffer to hold the converted data
static uint8_t oled_buffer[LCD_H_RES * LCD_V_RES / 8];

static i2c_master_bus_handle_t i2c_bus_handle = NULL;
static i2c_master_dev_handle_t i2c_dev_handle = NULL;

//------------------------------------------------------------------------------
// Functioon declarations
//------------------------------------------------------------------------------

void LED_init(void);
void LED_ON(void);
void LED_OFF(void);

void SSD1306_send_command(uint8_t cmd);
void SSD1306_send_data(uint8_t data);
void SSD1306_send_data_block(uint8_t* P_data, uint32_t numData);

//------------------------------------------------------------------------------

void app_main(void)
{
    ESP_LOGI(TAG, "Main start");

    // Create LED driver
	LED_init();

	for( int i=0; i<10; i++ )
	{
		vTaskDelay(pdMS_TO_TICKS(200));
		LED_OFF();
		vTaskDelay(pdMS_TO_TICKS(200));
		LED_ON();
	}

    // Create SDD1306 RST GPIO
	gpio_config_t SSD1306_RST_gpio_config =
    {
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = 1ULL << I2C_PIN_NUM_RST,
    };
    ESP_ERROR_CHECK(gpio_config(&SSD1306_RST_gpio_config));
    gpio_set_level(I2C_PIN_NUM_RST, 1);
    ESP_LOGI(TAG,"SSD1306_Init: SSD1306 RST set to high");

    // Setup I2C bus
    i2c_master_bus_config_t bus_config = 
    {
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .glitch_ignore_cnt = 7,
        .i2c_port = I2C_BUS_PORT,
        .sda_io_num = I2C_PIN_NUM_SDA,
        .scl_io_num = I2C_PIN_NUM_SCL,
        .flags.enable_internal_pullup = true,
    };
    ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &i2c_bus_handle));
    ESP_LOGI(TAG, "I2C bus created");

    // I2C device config and attach to I2C bus
    i2c_device_config_t i2c_device_config =
    {
        .dev_addr_length = I2C_ADDR_BIT_LEN_7,
        .device_address  = I2C_HW_ADDR,
        .scl_speed_hz    = (100 * 1000),
        .scl_wait_us     = 0,
    };
    ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_bus_handle, &i2c_device_config, &i2c_dev_handle));
    ESP_LOGI(TAG, "SSD1306 added to bus");
  
    vTaskDelay(pdMS_TO_TICKS(1000));
  
    // Reset OLED
    gpio_set_level(GPIO_OLED_RST, 0);
    vTaskDelay(pdMS_TO_TICKS(1000));
    gpio_set_level(GPIO_OLED_RST, 1);
    vTaskDelay(pdMS_TO_TICKS(1000));

    //Init OLED

    SSD1306_send_command(0xAE); // Display off
    SSD1306_send_command(0xD5); SSD1306_send_command(0x80);
    SSD1306_send_command(0xA8); SSD1306_send_command(0x3F);
    SSD1306_send_command(0xD3); SSD1306_send_command(0x00);
    SSD1306_send_command(0x40);
    SSD1306_send_command(0x8D); SSD1306_send_command(0x14);
    SSD1306_send_command(0x20); SSD1306_send_command(0x00); // Horizontal addressing mode
    SSD1306_send_command(0xA1);
    SSD1306_send_command(0xC8);
    SSD1306_send_command(0xDA); SSD1306_send_command(0x12);
    SSD1306_send_command(0x81); SSD1306_send_command(0xCF);
    SSD1306_send_command(0xD9); SSD1306_send_command(0xF1);
    SSD1306_send_command(0xDB); SSD1306_send_command(0x40);
    SSD1306_send_command(0xA4);
    SSD1306_send_command(0xA6);
    SSD1306_send_command(0xAF); // Display on

    ESP_LOGI(TAG, "SSD1306 initialization done");
    SSD1306_send_command(0x21);
    SSD1306_send_command(0);
    SSD1306_send_command(127);
    
    SSD1306_send_command(0x22);
    SSD1306_send_command(0);
    SSD1306_send_command(0);

    // Draw a pattern
    uint8_t pattern[128];
    for(int i=0; i<128; i++)
    {
        pattern[i] = (i%2 == 0) ? 0xAA : 0x55;
    }

    SSD1306_send_data_block(pattern, sizeof(pattern));
  
    ESP_LOGI(TAG, "SSD1306 pattern drawn");
}

//------------------------------------------------------------------------------

void SSD1306_send_command(uint8_t cmd)
{

  uint8_t buffer[2];
  buffer[0] = 0x00;
  buffer[1] = cmd;
  ESP_ERROR_CHECK(i2c_master_transmit(i2c_dev_handle, buffer, 2, pdMS_TO_TICKS(1000)));
  vTaskDelay(pdMS_TO_TICKS(100));
}

//------------------------------------------------------------------------------

void SSD1306_send_data(uint8_t data)
{
  uint8_t buffer[2];
  buffer[0] = 0x40;
  buffer[1] = data;
  ESP_ERROR_CHECK(i2c_master_transmit(i2c_dev_handle, buffer, 2, pdMS_TO_TICKS(1000)));
  vTaskDelay(pdMS_TO_TICKS(100));
}

//------------------------------------------------------------------------------

void SSD1306_send_data_block(uint8_t* P_data, uint32_t numData)
{
  uint8_t* buffer = malloc(numData + 1);
  buffer[0] = 0x40;  // Control byte for data
  memcpy(&buffer[1], P_data, numData);
  ESP_ERROR_CHECK(i2c_master_transmit(i2c_dev_handle, buffer, numData + 1, pdMS_TO_TICKS(1000)));
  free(buffer);
  vTaskDelay(pdMS_TO_TICKS(100));
}

//------------------------------------------------------------------------------


// This function creates a GPIO output to control a LED.

void LED_init(void)
{
	// Create LED GPIO
	gpio_config_t LED_gpio_config =
    {
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = 1ULL << GPIO_LED,
    };
    ESP_ERROR_CHECK(gpio_config(&LED_gpio_config));
    gpio_set_level(GPIO_LED, 1);
    ESP_LOGI(TAG,"LED control, output set to high");
}

//------------------------------------------------------------------------------

// This function sets the LED ON
void LED_ON(void)
{
	gpio_set_level(GPIO_LED, 1);
}

//------------------------------------------------------------------------------

// This function sets the LED OFF
void LED_OFF(void)
{
	gpio_set_level(GPIO_LED, 0);
}

//------------------------------------------------------------------------------
