/*
? [2026] Microchip Technology Inc. and its subsidiaries.

    Subject to your compliance with these terms, you may use Microchip 
    software and any derivatives exclusively with Microchip products. 
    You are responsible for complying with 3rd party license terms  
    applicable to your use of 3rd party software (including open source  
    software) that may accompany Microchip software. SOFTWARE IS ?AS IS.? 
    NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS 
    SOFTWARE, INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT,  
    MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 
    WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, 
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY 
    KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF 
    MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE 
    FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP?S 
    TOTAL LIABILITY ON ALL CLAIMS RELATED TO THE SOFTWARE WILL NOT 
    EXCEED AMOUNT OF FEES, IF ANY, YOU PAID DIRECTLY TO MICROCHIP FOR 
    THIS SOFTWARE.
*/
#include "mcc_generated_files/system/system.h"
#include "mcc_generated_files/system/pins.h"
#include "mcc_generated_files/timer/tmr1.h"
#include "mcc_generated_files/adc/adc4.h"
#include "mcc_generated_files/i2c_host/i2c1.h"
#include "mcc_generated_files/pwm/sccp1.h"
#include "mcc_generated_files/can/can1.h"
#include "bme280_app.h"
#include "pac1952_app.h"
#include "ssd1306_oled.h"
#include "st7789.h"
#include "clbwebinar200x200.h"
#include "webinar200x200.h"
#include "ws2812.h"
#include <string.h>
#include <stdio.h>

#define APP_CAN_TX_MSG_ID                0x100U
#define APP_CAN_TX_DLC_BYTES             8U
#define APP_TMR1_TICKS_PER_2S            20U
#define APP_TMR1_TICKS_PER_500MS         (APP_TMR1_TICKS_PER_2S / 4U)
#define APP_SAMPLE_PERIOD_SECONDS        2.0f
#define APP_ADC_WAIT_LIMIT               100000U
#define APP_I2C_WAIT_LIMIT               100000U
#define APP_WS2812_BRIGHTNESS_PERCENT    5U
#define APP_DELAY_1MS_ITERATIONS         100000UL
#define APP_POWER_ON_DELAY_MS            100U
#define APP_MCP9800A5_I2C_ADDRESS        0x4DU
#define APP_MCP9800_REG_TEMP             0x00U
#define APP_TFT_BOOT_TEXT                "Microchip 2026"
#define APP_TFT_BOOT_TEXT_CHAR_COUNT     14U
#define APP_TFT_FONT8X8_CELL_SIZE        8U
#define APP_TFT_BOOT_TEXT_SCALE          2U
#define APP_TFT_IMAGE_X                  ((ST7789_WIDTH - WEBINAR200X200_WIDTH) / 2U)
#define APP_TFT_IMAGE_Y                  (((ST7789_HEIGHT - WEBINAR200X200_HEIGHT) / 2U) - 10U)

static volatile uint8_t tmr1TickCount = 0;

static bool APP_ADC4_ReadChannel0(uint16_t *adcValue)
{
    uint32_t guardCount = 0U;

    if(adcValue == NULL)
    {
        return false;
    }

    ADC4_ChannelSoftwareTriggerEnable(ADC4_Channel0);

    while(!ADC4_IsConversionComplete(ADC4_Channel0))
    {
        guardCount++;
        if(guardCount > APP_ADC_WAIT_LIMIT)
        {
            return false;
        }
    }

    *adcValue = (uint16_t)ADC4_ConversionResultGet(ADC4_Channel0);
    return true;
}

static bool APP_MCP9800A5_ReadTemperatureC(float *temperatureC)
{
    uint8_t reg = APP_MCP9800_REG_TEMP;
    uint8_t raw[2] = {0U, 0U};
    uint32_t guardCount = 0U;
    int16_t tempRaw;

    if(temperatureC == NULL)
    {
        return false;
    }

    while(I2C1_IsBusy())
    {
        guardCount++;
        if(guardCount > APP_I2C_WAIT_LIMIT)
        {
            return false;
        }
    }

    if(!I2C1_Host_WriteRead(APP_MCP9800A5_I2C_ADDRESS, &reg, 1U, raw, sizeof(raw)))
    {
        return false;
    }

    guardCount = 0U;
    while(I2C1_IsBusy())
    {
        guardCount++;
        if(guardCount > APP_I2C_WAIT_LIMIT)
        {
            return false;
        }
    }

    if(I2C1_Host_ErrorGet() != I2C_HOST_ERROR_NONE)
    {
        return false;
    }

    tempRaw = (int16_t)(((uint16_t)raw[0] << 8) | raw[1]);
    *temperatureC = ((float)tempRaw) / 256.0f;
    return true;
}

static void APP_Timer1TimeoutCallback(void)
{
    tmr1TickCount++;
}

static void APP_DelayMs(uint16_t delayMs)
{
    volatile uint32_t i;
    uint16_t ms;

    for(ms = 0U; ms < delayMs; ms++)
    {
        for(i = 0UL; i < APP_DELAY_1MS_ITERATIONS; i++)
        {
            __builtin_nop();
        }
    }
}

static void APP_WS2812_SetSequenceColor(uint8_t colorIndex)
{
    switch(colorIndex)
    {
        case 0U:
            WS2812_SetColorRGB(0xFFU, 0x00U, 0x00U);
            break;
        case 1U:
            WS2812_SetColorRGB(0x00U, 0xFFU, 0x00U);
            break;
        case 2U:
            WS2812_SetColorRGB(0x00U, 0x00U, 0xFFU);
            break;
        case 3U:
            WS2812_SetColorRGB(0xFFU, 0xFFU, 0x00U);
            break;
        case 4U:
            WS2812_SetColorRGB(0xFFU, 0xFFU, 0xFFU);
            break;
        default:
            WS2812_SetColorRGB(0xFFU, 0x00U, 0x00U);
            break;
    }
}

static void APP_RGB_Module_SetOneHot(uint8_t colorIndex)
{
    LED_R_SetLow();
    LED_G_SetLow();
    LED_B_SetLow();

    switch(colorIndex)
    {
        case 0U:
            LED_R_SetHigh();
            break;
        case 1U:
            LED_G_SetHigh();
            break;
        default:
            LED_B_SetHigh();
            break;
    }
}

static void APP_RGB_Module_AllOff(void)
{
    LED_R_SetLow();
    LED_G_SetLow();
    LED_B_SetLow();
}

static enum CAN_TX_MSG_REQUEST_STATUS APP_CAN1_TransmitADCResult(uint16_t adcValue)
{
    uint8_t txData[APP_CAN_TX_DLC_BYTES];
    struct CAN_MSG_OBJ txMsg;

    (void)memset(txData, 0, sizeof(txData));
    txData[0] = (uint8_t)(adcValue >> 8);
    txData[1] = (uint8_t)(adcValue & 0xFFU);

    txMsg.msgId = APP_CAN_TX_MSG_ID;
    txMsg.field.idType = (unsigned int)CAN_FRAME_STD;
    txMsg.field.frameType = (unsigned int)CAN_FRAME_DATA;
    txMsg.field.dlc = (unsigned int)DLC_8;
    txMsg.field.formatType = (unsigned int)CAN_2_0_FORMAT;
    txMsg.field.brs = (unsigned int)CAN_NON_BRS_MODE;
    txMsg.data = txData;

    return CAN1_Transmit(CAN1_TX_TXQ, &txMsg);
}

enum APP_OLED_DISPLAY_MODE
{
    APP_OLED_DISPLAY_CURRENT = 0,
    APP_OLED_DISPLAY_ENERGY,
    APP_OLED_DISPLAY_BME280
};

int main(void)
{
    uint8_t elapsedTicks;
    uint8_t ledTickAccumulator = 0;
    uint8_t pacTickAccumulator = 0;
    float currentCh1_A;
    float currentCh2_A;
    float energyCh1_Wh;
    float energyCh2_Wh;
    uint8_t oledDisplayMode = APP_OLED_DISPLAY_CURRENT;
    bool buzzerPwmEnabled = false;
    bool tftShowClbImage = false;
    bool bme280Ready = false;
    bool bme280Calibrated = false;
    uint8_t ws2812ColorIndex = 0U;
    uint8_t rgbModuleColorIndex = 0U;
    uint16_t vr1AdcValue;
    float mcp9800TemperatureC;
    float bme280TemperatureC;
    float bme280HumidityPct;
    float bme280PressurehPa;
    char oledLine2[17];
    char oledLine3[17];
    char oledLine4[17];

    SYSTEM_Initialize();
    APP_DelayMs(APP_POWER_ON_DELAY_MS);

    Timer1_TimeoutCallbackRegister(APP_Timer1TimeoutCallback);
    LED1_SetLow();
    PWM1_Disable();
    ST7789_Initialize();
    ST7789_FillRect(0U, 0U, ST7789_WIDTH, ST7789_HEIGHT, ST7789_COLOR_WHITE);
    ST7789_DrawImageRGB565(APP_TFT_IMAGE_X,
                           APP_TFT_IMAGE_Y,
                           WEBINAR200X200_WIDTH,
                           WEBINAR200X200_HEIGHT,
                           webinar200x200_rgb565);
    ST7789_DrawString8x8((ST7789_WIDTH - (APP_TFT_BOOT_TEXT_CHAR_COUNT * APP_TFT_FONT8X8_CELL_SIZE * APP_TFT_BOOT_TEXT_SCALE)) / 2U,
                         ST7789_HEIGHT - (APP_TFT_FONT8X8_CELL_SIZE * APP_TFT_BOOT_TEXT_SCALE)-10,
                         APP_TFT_BOOT_TEXT,
                         ST7789_COLOR_BLUE,
                         ST7789_COLOR_WHITE,
                         APP_TFT_BOOT_TEXT_SCALE);
    PAC1952_AppInitialize();
    bme280Ready = BME280_AppInitialize();
    if(!bme280Ready)
    {
        printf("BME280 init fail: addr=0x%02X chip=0x%02X err=%s\r\n",
               (unsigned int)BME280_AppGetAddress(),
               (unsigned int)BME280_AppGetLastChipId(),
               BME280_AppGetLastErrorString());
    }
    else
    {
        bme280Calibrated = BME280_AppCalibrate();
        if(!bme280Calibrated)
        {
            printf("BME280 calibration fail: addr=0x%02X chip=0x%02X err=%s\r\n",
                   (unsigned int)BME280_AppGetAddress(),
                   (unsigned int)BME280_AppGetLastChipId(),
                   BME280_AppGetLastErrorString());
        }
        printf("BME280 ready: addr=0x%02X chip=0x%02X\r\n",
               (unsigned int)BME280_AppGetAddress(),
               (unsigned int)BME280_AppGetLastChipId());
    }
    (void)SSD1306_OLED_Initialize();
    (void)SSD1306_OLED_WriteLine(0U, "Hello Microchip");
    WS2812_Initialize();
    WS2812_SetBrightnessPercent(APP_WS2812_BRIGHTNESS_PERCENT);
    APP_WS2812_SetSequenceColor(ws2812ColorIndex);
    APP_RGB_Module_AllOff();
    PWM1_Disable();

    while(1)
    {
        if(SW1_GetValue() == 0U)
        {
            if(!buzzerPwmEnabled)
            {
                PWM1_Enable();
                buzzerPwmEnabled = true;
            }
        }
        else
        {
            if(buzzerPwmEnabled)
            {
                PWM1_Disable();
                buzzerPwmEnabled = false;
            }
        }

        if (SW2_GetValue() == 0U)
        {
            LED2_SetLow();
        }

        else
        {
            LED2_SetHigh();
        }


        __builtin_disable_interrupts();
        elapsedTicks = tmr1TickCount;
        tmr1TickCount = 0;
        __builtin_enable_interrupts();

        while(elapsedTicks > 0U)
        {
            elapsedTicks--;

            ledTickAccumulator++;
            pacTickAccumulator++;

            if(ledTickAccumulator >= APP_TMR1_TICKS_PER_500MS)
            {
                ledTickAccumulator = 0;
                LED1_Toggle();

                ws2812ColorIndex++;
                if(ws2812ColorIndex >= 5U)
                {
                    ws2812ColorIndex = 0U;
                }
                APP_WS2812_SetSequenceColor(ws2812ColorIndex);

                if(SW2_GetValue() == 0U)
                {
                    rgbModuleColorIndex++;
                    if(rgbModuleColorIndex >= 3U)
                    {
                        rgbModuleColorIndex = 0U;
                    }
                    APP_RGB_Module_SetOneHot(rgbModuleColorIndex);
                }
                else
                {
                    APP_RGB_Module_AllOff();
                }

                if(APP_ADC4_ReadChannel0(&vr1AdcValue))
                {
                    if(APP_MCP9800A5_ReadTemperatureC(&mcp9800TemperatureC))
                    {
                        (void)snprintf(oledLine2, sizeof(oledLine2), "VR1=%4u T=%4.1fC", (unsigned int)vr1AdcValue, mcp9800TemperatureC);
                    }
                    else
                    {
                        (void)snprintf(oledLine2, sizeof(oledLine2), "VR1=%4u T=--.-C", (unsigned int)vr1AdcValue);
                    }
                    (void)SSD1306_OLED_WriteLine(1U, oledLine2);
                    (void)APP_CAN1_TransmitADCResult(vr1AdcValue);
                }
            }

            if(pacTickAccumulator >= APP_TMR1_TICKS_PER_2S)
            {
                pacTickAccumulator = 0;
                PAC1952_AppReadAndPrint(APP_SAMPLE_PERIOD_SECONDS);

                tftShowClbImage = !tftShowClbImage;
                if(tftShowClbImage)
                {
                    ST7789_DrawImageRGB565(APP_TFT_IMAGE_X,
                                           APP_TFT_IMAGE_Y,
                                           CLBWEBINAR200X200_WIDTH,
                                           CLBWEBINAR200X200_HEIGHT,
                                           clbwebinar200x200_rgb565);
                }
                else
                {
                    ST7789_DrawImageRGB565(APP_TFT_IMAGE_X,
                                           APP_TFT_IMAGE_Y,
                                           WEBINAR200X200_WIDTH,
                                           WEBINAR200X200_HEIGHT,
                                           webinar200x200_rgb565);
                }

                if(oledDisplayMode == APP_OLED_DISPLAY_CURRENT)
                {
                    if(PAC1952_AppGetLastCurrents(&currentCh1_A, &currentCh2_A))
                    {
                        (void)snprintf(oledLine3, sizeof(oledLine3), "CH1 I=%+.4fA", currentCh1_A);
                        (void)snprintf(oledLine4, sizeof(oledLine4), "CH2 I=%+.4fA", currentCh2_A);
                        (void)SSD1306_OLED_WriteLine(2U, oledLine3);
                        (void)SSD1306_OLED_WriteLine(3U, oledLine4);
                    }
                }
                else if(oledDisplayMode == APP_OLED_DISPLAY_ENERGY)
                {
                    if(PAC1952_AppGetLastEnergy(&energyCh1_Wh, &energyCh2_Wh))
                    {
                        (void)snprintf(oledLine3, sizeof(oledLine3), "CH1 E=%.3fmWh", energyCh1_Wh * 1000.0f);
                        (void)snprintf(oledLine4, sizeof(oledLine4), "CH2 E=%.3fmWh", energyCh2_Wh * 1000.0f);
                        (void)SSD1306_OLED_WriteLine(2U, oledLine3);
                        (void)SSD1306_OLED_WriteLine(3U, oledLine4);
                    }
                }
                else
                {
                    if(bme280Ready && bme280Calibrated && BME280_AppRead(&bme280TemperatureC, &bme280HumidityPct, &bme280PressurehPa))
                    {
                        (void)snprintf(oledLine3, sizeof(oledLine3), "T=%4.1fC H=%4.1f%%", bme280TemperatureC, bme280HumidityPct);
                        (void)snprintf(oledLine4, sizeof(oledLine4), "P=%7.1fhPa", bme280PressurehPa);
                    }
                    else
                    {
                        (void)snprintf(oledLine3, sizeof(oledLine3), "BME280 read fail");
                        (void)snprintf(oledLine4, sizeof(oledLine4), "A=%02X %s",
                                       (unsigned int)BME280_AppGetAddress(),
                                       BME280_AppGetLastErrorString());
                        printf("BME280 read fail: addr=0x%02X chip=0x%02X err=%s\r\n",
                               (unsigned int)BME280_AppGetAddress(),
                               (unsigned int)BME280_AppGetLastChipId(),
                               BME280_AppGetLastErrorString());
                    }

                    (void)SSD1306_OLED_WriteLine(2U, oledLine3);
                    (void)SSD1306_OLED_WriteLine(3U, oledLine4);
                }

                oledDisplayMode++;
                if(oledDisplayMode > APP_OLED_DISPLAY_BME280)
                {
                    oledDisplayMode = APP_OLED_DISPLAY_CURRENT;
                }
            }
        }
    }
}
