#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

// 딜레이 함수 이용을 위해
#include <windows.h>
//Sleep(1);   // 1ms 딜레이
//Sleep(10);  // 10ms 딜레이
//Sleep(100); // 100ms 딜레이

// FT4222 헤더
#include "ftd2xx.h"
#include "LibFT4222.h"

uint8_t PMIC_I2C_SLAVE_ADDR = 0x55;

// 현재 시간을 "HH:MM:SS" 형식으로 buf에 저장
void currentTime(char* buf, size_t len)
{
    if (buf == nullptr || len == 0)
        return;

    time_t now = time(nullptr);
    struct tm tm_buf;

#ifdef _WIN32
    localtime_s(&tm_buf, &now);   // Windows 안전 버전
#else
    localtime_r(&now, &tm_buf);   // Linux/Unix 안전 버전
#endif

    strftime(buf, len, "%H:%M:%S", &tm_buf);
}

// 로그 출력 (커스텀 포맷)
void logPacket(const char* dir, uint8_t* data, int size, int hasCrc)
{
    if (dir == nullptr || data == nullptr || size <= 0)
        return;

    char timeBuf[32];
    currentTime(timeBuf, sizeof(timeBuf));

    // PMIC I2C WRITE
    if (strcmp(dir, "PMIC I2C WRITE") == 0 && size >= 3) {
        printf("[%s] [%s] len=%d  [Address: 0x%02X] [Value: 0x%02X] [CRC: 0x%02X]\n",
            timeBuf, dir, size, data[0], data[1], data[2]);
        return;
    }

    // PMIC I2C READ
    if (strcmp(dir, "PMIC I2C READ") == 0 && size >= 2) {
        printf("[%s] [%s] len=%d  [Value: 0x%02X] [CRC: 0x%02X]\n",
            timeBuf, dir, size, data[0], data[1]);
        return;
    }

    // 기본 출력 (기타 상황)
    printf("[%s] [%s] len=%d  ", timeBuf, dir, size);

    for (int i = 0; i < size; i++) {
        if (hasCrc && i == size - 1)
            printf("[CRC: %02X] ", data[i]);
        else
            printf("%02X ", data[i]);
    }

    printf("\n");
}

// CRC8 계산 (다항식 0x07, 초기값 0x00)
uint8_t calcCRC8(uint8_t addrWithRw, uint8_t* data, int len)
{
    uint8_t crc = 0x00;

    for (int j = -1; j < len; j++) {
        uint8_t b = (j == -1) ? addrWithRw : data[j];
        crc ^= b;
        for (int i = 0; i < 8; i++) {
            if (crc & 0x80)
                crc = (crc << 1) ^ 0x07;
            else
                crc <<= 1;
        }
    }
    return crc;
}

// 명령어 판별 (I2C_WRITE/w, I2C_READ/r)
int parseCommand(const char* dir)
{
    if (strcmp(dir, "I2C_WRITE") == 0 || strcmp(dir, "W") == 0)
        return 1;
    if (strcmp(dir, "I2C_READ") == 0 || strcmp(dir, "R") == 0)
        return 2;
    return 0;
}

// Address에 1바이트 Write with CRC, 그리고 Address를 Read해서 Write한 값과 비교
void PMIC_I2C_Write(FT_HANDLE ftHandle, uint8_t slaveAddr, uint8_t regAddr, uint8_t value)
{
    uint8_t bytes[3];
    int len = 0;

    // Write 패킷: [RegAddr] [Value] [CRC]
    bytes[len++] = regAddr;
    bytes[len++] = value;

    uint8_t addrWithRw = (slaveAddr << 1) | 0x00; // Write 주소
    uint8_t crc = calcCRC8(addrWithRw, bytes, len);
    bytes[len++] = crc;

    uint16 transferred = 0;
    FT4222_STATUS st = FT4222_I2CMaster_Write(ftHandle,
        slaveAddr,
        bytes,
        (uint16)len,
        &transferred);

    if (st != FT4222_OK) {
        printf("I2C Write 실패\n");
        return;
    }
    else {
        logPacket("PMIC I2C WRITE", bytes, len, 1);
    }

    // Register Address 다시 Write (Read 시작 위치 지정)
    uint8_t regOnly[1] = { regAddr };
    st = FT4222_I2CMaster_Write(ftHandle,
        slaveAddr,
        regOnly,
        1,
        &transferred);

    if (st != FT4222_OK) {
        printf("Read용 Address Write 실패\n");
        return;
    }

    // Register Address에서 2바이트 Read
    uint8_t rx[2] = { 0 };
    st = FT4222_I2CMaster_Read(ftHandle,
        slaveAddr,
        rx,
        2,
        &transferred);

    if (st != FT4222_OK) {
        printf("I2C Read 실패\n");
        return;
    }
    else {
        logPacket("PMIC I2C READ ", rx, transferred, 0);

        // 첫 번째 바이트와 Write 값 비교
        if (rx[0] == value) {
            printf("확인 성공: [Write Value = 0x%02X], [Read Value = 0x%02X]\n",
                value, rx[0]);
        }
        else {
            printf("불일치: [Write Value = 0x%02X], [Read Value = 0x%02X]\n",
                value, rx[0]);
        }
    }
}

// Address에서 1바이트 Read (CRC 계산 없음)
void PMIC_I2C_Read(FT_HANDLE ftHandle, uint8_t slaveAddr, uint8_t regAddr)
{
    uint16 transferred = 0;
    FT4222_STATUS st;

    // 🔹 Register Address를 먼저 Write (Read 시작 위치 지정)
    uint8_t regOnly[1] = { regAddr };
    st = FT4222_I2CMaster_Write(ftHandle,
        slaveAddr,
        regOnly,
        1,
        &transferred);

    if (st != FT4222_OK) {
        printf("Read용 Address Write 실패\n");
        return;
    }

    // 🔹 Register Address에서 2바이트 Read (Value + CRC)
    uint8_t rx[2] = { 0 };
    st = FT4222_I2CMaster_Read(ftHandle,
        slaveAddr,
        rx,
        2,
        &transferred);

    if (st != FT4222_OK) {
        printf("I2C Read 실패\n");
        return;
    }
    else {
        printf("Read 결과: [Address = 0x%02X], [Value = 0x%02X], [CRC = 0x%02X]\n",
            regAddr, rx[0], rx[1]);
    }
}

int main()
{
    printf("PMIC I2C Packet Logger with FT4222\n \n");

    char addrInput[16];
    unsigned int tempAddr = 0;
    uint8_t I2C_SLAVE_ADDR = 0;
    unsigned long log_counter = 0;

    char timeStr[16];   // 시간을 저장할 버퍼 (HH:MM:SS면 9바이트면 충분)

    currentTime(timeStr, sizeof(timeStr));

    //printf("현재 시간: %s\n", timeStr);


#if 0
    printf("I2C Address\n Regulation Register is 0x54\n Protection Register is 0x55\n \nEnter 7-bit I2C Slave Address (hex): ");
    if (scanf_s("%15s", addrInput, (unsigned)_countof(addrInput)) != 1) {
        printf("입력 오류\n");
        return -1;
    }
    if (sscanf_s(addrInput, "%x", &tempAddr) != 1) {
        printf("잘못된 주소 입력\n");
        return -1;
    }
    I2C_SLAVE_ADDR = (uint8_t)tempAddr;

    printf("슬레이브 주소 설정됨: 0x%02X\n", I2C_SLAVE_ADDR);

    getchar(); // 버퍼 클리어

    printf("사용법: I2C_WRITE <data...> 또는 w <data...>\n");
    printf("       I2C_READ (항상 2바이트 읽음) 또는 r\n");
    printf("exit 또틑 e 입력 시 종료\n");
#endif

    printf("Read Regulation 0x54, Page 0x02, Address 0x23 : 0x66 이면 OK\n ");

    FT_HANDLE ftHandle = NULL;
    FT_STATUS ftStatus;
    DWORD numDevs = 0;

    ftStatus = FT_CreateDeviceInfoList(&numDevs);
    if (ftStatus != FT_OK || numDevs == 0) {
        printf("FT4222 장치를 찾을 수 없습니다.\n");
        return -1;
    }

    ftStatus = FT_Open(0, &ftHandle);
    if (ftStatus != FT_OK) {
        printf("FT4222 장치 열기 실패\n");
        return -1;
    }

    FT4222_STATUS ft4222Status = FT4222_I2CMaster_Init(ftHandle, 400); // 400kHz
    if (ft4222Status != FT4222_OK) {
        printf("I2C Master 초기화 실패\n");
        FT_Close(ftHandle);
        return -1;
    }

    //Fault register data: 0x010 ~0x014, 0x019 ~0x021
    //0x010 - FUSA_STATUS_1
    //0x011 - FUSA_STATUS_2
    //0x012 - FUSA_STATUS_2A
    //0x013 - FUSA_STATUS_3
    //0x014 - FUSA_STATUS_4
    //0x019 - FLT_RECORD_A
    //0x01A - FLT_RECORD_B
    //0x01B - FLT_RECORD_GND_AVIN
    //0x01C - FLT_RECORD_BG_Temp
    //0x01D - FLT_RECORD_IntLDOs
    //0x01E - FLT_RECORD_ExtLDOs
    //0x01F - FLT_RECORD_BUCKS_B
    //0x020 - FLT_RECORD_BUCKS_A
    //0x021 - FLT_RECORD_ExtINPs_7_0

    //printf("=================================================================================\n");
    /********************************************************************************************
    I2C Write
    PMIC_I2C_Write(FT_HANDLE ftHandle, uint8_t slaveAddr, uint8_t regAddr, uint8_t value)
    PMIC_I2C_Write(ftHandle, PMIC_I2C_SLAVE_ADDR, 0xAA, 0xBB);
    ********************************************************************************************/
    //printf("=================================================================================\n");
    /********************************************************************************************
    I2C Read
    PMIC_I2C_Read(FT_HANDLE ftHandle, uint8_t slaveAddr, uint8_t regAddr)
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0xAA);
    ********************************************************************************************/



    //for (int i = 0; i < 10000000; i++) {
    //while (1) {
    //}

    //for (int i = 0; i < 10000000; i++) {
    //while (log_counter < 10 ){

        // 예: 10번 while에 한 번만 실행
        //if (log_counter % 10 == 0) {
            // 30ms 딜레이
            //Sleep(30);

    printf("실행하려면 Enter 키를 누르세요...\n");
    getchar();

    //Regulation Register
    // Page00
    PMIC_I2C_Write(ftHandle, 0x54, 0x00, 0x00);
    PMIC_I2C_Write(ftHandle, 0x54, 0x00, 0x00);

    PMIC_I2C_Read(ftHandle, 0x54, 0x00); //0x000 - IO_PAGE_REGU
    PMIC_I2C_Read(ftHandle, 0x54, 0x01); //0x001 - IO_CHIPNAME_REGU
    PMIC_I2C_Read(ftHandle, 0x54, 0x02); //0x002 - IO_CHIPVERSION_REGU
    PMIC_I2C_Read(ftHandle, 0x54, 0x03); //0x003 - IO_DIEID3_REGU
    //PMIC_I2C_Read(ftHandle, 0x54, 0x04); //0x004
    //PMIC_I2C_Read(ftHandle, 0x54, 0x05); //0x005

    //PMIC_I2C_Write(ftHandle, 0x54, 0x00, 0x02);
    //PMIC_I2C_Write(ftHandle, 0x54, 0x00, 0x02);
    //PMIC_I2C_Read(ftHandle, 0x54, 0x23);

    // 30ms 딜레이
    //Sleep(30);

    // Page02
    PMIC_I2C_Write(ftHandle, 0x54, 0x00, 0x02);
    PMIC_I2C_Write(ftHandle, 0x54, 0x00, 0x02);
    //PMIC_I2C_Read(ftHandle, 0x54, 0x20);
    //PMIC_I2C_Read(ftHandle, 0x54, 0x21);
    PMIC_I2C_Read(ftHandle, 0x54, 0x22); //0x222 - I_OTP_VERSION
    PMIC_I2C_Read(ftHandle, 0x54, 0x23); //0x223 - I_OTP_INTERNALVERSION
    //PMIC_I2C_Read(ftHandle, 0x54, 0x24);
    //PMIC_I2C_Read(ftHandle, 0x54, 0x25);

    // 30ms 딜레이
    //Sleep(30);

    //Protection Register
    // Page01
    PMIC_I2C_Write(ftHandle, 0x55, 0x00, 0x01);
    PMIC_I2C_Write(ftHandle, 0x55, 0x00, 0x01);
    PMIC_I2C_Read(ftHandle, 0x55, 0x02); //0x102 - IO_MODECTRL Read시 디폴트 0x65
    PMIC_I2C_Write(ftHandle, 0x55, 0x02, 0x25); //0x102 - IO_MODECTRL

    // 10s 딜레이
    Sleep(10000);

    PMIC_I2C_Write(ftHandle, 0x55, 0x02, 0x65); //0x102 - IO_MODECTRL

    //}
    //log_counter++;
//}

#if 0
    //Protection Register
    //Regulation Register
    printf("[Page-0]=================================================================================\n");
    PMIC_I2C_Write(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x00, 0x00);
    PMIC_I2C_Write(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x00, 0x00);

    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x00); //0x000 - IO_PAGE_REGU
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x01); //0x001 - IO_CHIPNAME_REGU
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x02); //0x002 - IO_CHIPVERSION_REGU
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x03); //0x003 - IO_DIEID3_REGU
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x04); //0x004 - IO_DIEID2_REGU
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x05); //0x005 - IO_DIEID1_REGU
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x06); //0x006 - IO_DIEID0_REGU
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x0A); //0x00A - OTP_VERSION_REGU

    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x10); //0x010 - FUSA_STATUS_1
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x11); //0x011 - FUSA_STATUS_2
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x12); //0x012 - FUSA_STATUS_2A
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x13); //0x013 - FUSA_STATUS_3
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x14); //0x014 - FUSA_STATUS_4
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x19); //0x019 - FLT_RECORD_A
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x1A); //0x01A - FLT_RECORD_B
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x1B); //0x01B - FLT_RECORD_GND_AVIN
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x1C); //0x01C - FLT_RECORD_BG_Temp
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x1D); //0x01D - FLT_RECORD_IntLDOs
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x1E); //0x01E - FLT_RECORD_ExtLDOs
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x1F); //0x01F - FLT_RECORD_BUCKS_B


    printf("[Page-1]=================================================================================\n");
    PMIC_I2C_Write(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x00, 0x01);
    PMIC_I2C_Write(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x00, 0x01);


    printf("[Page-2]=================================================================================\n");
    PMIC_I2C_Write(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x00, 0x02);
    PMIC_I2C_Write(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x00, 0x02);
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x20); //0x020 - FLT_RECORD_BUCKS_A
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x21); //0x021 - FLT_RECORD_ExtINPs_7_0


    printf("[PMIC_RAA271005_INIT]====================================================================\n");
    PMIC_I2C_Write(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x00, 0x01);
    PMIC_I2C_Write(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x00, 0x01);

    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x07); //0x107 - WDT_CFG0 (OTP) 
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x08); //0x108 - WDT_CFG1 (OTP) //WDT_ULCNT, WDT_LLCNT
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x09); //0x109 - WDT_CFG2 (OTP)
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x16); //0x116 - FUSA_TIMER_1 (OTP)
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x17); //0x117 - FUSA_TIMER_2 (OTP)
    PMIC_I2C_Read(ftHandle, PMIC_I2C_SLAVE_ADDR, 0x2C); //0x12C - FLT_MASK_B (OTP)
#endif
    char line[256];
    while (1) {
        printf("> ");
        if (!fgets(line, sizeof(line), stdin)) break;

        line[strcspn(line, "\r\n")] = 0; // 개행 제거
        if (strcmp(line, "exit") == 0) break;
        if (strcmp(line, "e") == 0) break;
        if (strlen(line) == 0) continue;

        char* context = NULL;
        char* token = strtok_s(line, " ", &context);
        if (!token) continue;

        for (char* p = token; *p; p++) *p = toupper(*p);

        int command = parseCommand(token);

        if (command == 1) { // I2C_WRITE
            uint8_t bytes[256];
            int len = 0;

            while ((token = strtok_s(NULL, " ", &context)) != NULL) {
                unsigned int val;
                if (sscanf_s(token, "%x", &val) == 1) {
                    bytes[len++] = (uint8_t)val;
                }
                else {
                    printf("잘못된 HEX 값: %s\n", token);
                }
            }

            if (len == 0) {
                printf("전송할 데이터가 없습니다.\n");
                continue;
            }

            uint8_t addrWithRw = (I2C_SLAVE_ADDR << 1) | 0x00;
            uint8_t crc = calcCRC8(addrWithRw, bytes, len);
            bytes[len++] = crc;

            uint16 transferred = 0;
            ft4222Status = FT4222_I2CMaster_Write(ftHandle,
                I2C_SLAVE_ADDR,
                bytes,
                (uint16)len,
                &transferred);

            if (ft4222Status != FT4222_OK) {
                printf("I2C Write 실패\n");
            }
            else {
                logPacket("I2C_WRITE", bytes, len, 1);
            }
        }
        else if (command == 2) { // I2C_READ
            uint8_t bytes[256];
            uint16 transferred = 0;
            FT4222_STATUS st;
            int len = 0;

            while ((token = strtok_s(NULL, " ", &context)) != NULL) {
                unsigned int val;
                if (sscanf_s(token, "%x", &val) == 1) {
                    bytes[len++] = (uint8_t)val;
                }
                else {
                    printf("잘못된 HEX 값: %s\n", token);
                }
            }

            if (len == 0) {
                printf("전송할 데이터가 없습니다.\n");
                continue;
            }

            uint8_t addrWithRw = (I2C_SLAVE_ADDR << 1) | 0x00;
            uint8_t crc = calcCRC8(addrWithRw, bytes, len);
            bytes[len++] = crc;

            st = FT4222_I2CMaster_Write(ftHandle,
                I2C_SLAVE_ADDR,
                bytes,
                (uint16)len,
                &transferred);

            if (st != FT4222_OK) {
                printf("Read용 Address Write 실패\n");
                return 0;
            }

            // 🔹 Register Address에서 2바이트 Read (Value + CRC)
            uint8_t rx[2] = { 0 };
            st = FT4222_I2CMaster_Read(ftHandle,
                I2C_SLAVE_ADDR,
                rx,
                2,
                &transferred);

            if (st != FT4222_OK) {
                printf("I2C Read 실패\n");
                return 0;
            }
            else {
                printf("Read 결과: [Address = 0x%02X], [Value = 0x%02X], [CRC = 0x%02X]\n",
                    bytes[0], rx[0], rx[1]);
            }
        }
        else {
            printf("첫 단어는 I2C_WRITE(w) 또는 I2C_READ(r) 여야 합니다.\n");
        }
    }

    FT_Close(ftHandle);
    return 0;
}
