/*
 * main.c 안의 카운팅 로직(load_counts / drive / HAL_TIM_PeriodElapsedCallback)을
 * 그대로 옮겨와 PC에서 돌려보는 검증용 프로그램.
 * MCU/HAL 없이도 "틱이 몇 번 지나면 LED가 몇 번 깜박이는가"가 설계값과
 * 정확히 일치하는지를 기계적으로 확인하기 위한 것.
 */
#include <stdio.h>
#include <stdint.h>

#define LED_MASK      0x01FFu
#define N_POINTS      9
#define COUNT_MIN     1
#define COUNT_MAX     15
#define TICKS_BURST   30
#define TICKS_IDLE    10
#define TICKS_CYCLE   (TICKS_BURST + TICKS_IDLE)

static uint16_t tick  = 0;
static uint16_t cycle = 0;
static uint8_t  cnt[N_POINTS];

/* --- 실제 main.c와 동일한 로직 --- */
static void load_counts(void)
{
    for (uint8_t k = 0; k < N_POINTS; k++) {
        cnt[k] = (uint8_t)(((cycle + k) % COUNT_MAX) + COUNT_MIN);
    }
}

static uint32_t drive_bsrr(uint16_t bits)
{
    /* 실제 GPIOC->BSRR 대신, 계산 결과만 반환 */
    return ((uint32_t)( bits & LED_MASK) << 16)
         | ((uint32_t)(~bits & LED_MASK));
}

/* 검증용 상태: 각 지점의 "실제 관측된" 상승 에지(꺼짐->켜짐) 횟수 */
static uint16_t rising_edges[N_POINTS];
static uint16_t prev_bits = 0;

static void tick_step(void)
{
    uint16_t bits = 0;

    if (tick < TICKS_BURST && (tick & 1u) == 0u) {
        for (uint8_t k = 0; k < N_POINTS; k++) {
            if (tick < (uint16_t)cnt[k] * 2u) {
                bits |= (uint16_t)(1u << k);
            }
        }
    }

    /* 검증: 상승 에지 카운트 (디코더가 세는 것과 동일한 방식) */
    for (uint8_t k = 0; k < N_POINTS; k++) {
        uint16_t m = (uint16_t)(1u << k);
        if ((bits & m) && !(prev_bits & m)) rising_edges[k]++;
    }
    prev_bits = bits;

    (void)drive_bsrr(bits); /* 레지스터 계산이 죽지 않는지만 확인 */

    if (++tick >= TICKS_CYCLE) {
        tick = 0;

        printf("CYCLE=%u 목표:", cycle + 1);
        for (uint8_t k = 0; k < N_POINTS; k++) printf(" G%u=%2u", k + 1, cnt[k]);
        printf("\n         실측:");
        int all_match = 1;
        for (uint8_t k = 0; k < N_POINTS; k++) {
            printf(" G%u=%2u", k + 1, rising_edges[k]);
            if (rising_edges[k] != cnt[k]) all_match = 0;
        }
        printf("   [%s]\n", all_match ? "일치" : "!! 불일치 !!");

        for (uint8_t k = 0; k < N_POINTS; k++) rising_edges[k] = 0;
        cycle++;
        load_counts();
    }
}

int main(void)
{
    load_counts();

    printf("=== 사이클 하나가 정확히 40틱(8.0s)인지 확인 ===\n");
    uint16_t total_ticks = 0;
    uint16_t start_cycle = cycle;
    while (cycle == start_cycle) { tick_step(); total_ticks++; }
    printf("1 사이클 = %u 틱 (기대값 %u)  ->  %s\n\n",
           total_ticks, (unsigned)TICKS_CYCLE,
           total_ticks == TICKS_CYCLE ? "OK" : "FAIL");

    printf("=== 15 사이클(= 모든 카운트 1~15를 모든 지점이 한 번씩 경험) 실행 ===\n");
    for (int i = 0; i < 15 * TICKS_CYCLE; i++) tick_step();

    printf("\n=== 카운트 0(=소등과 구분 안 되는 값)이 실제로 한 번도 나오지 않는지 확인 ===\n");
    int zero_seen = 0;
    for (int c = 0; c < 200; c++) {
        for (uint8_t k = 0; k < N_POINTS; k++) {
            if (cnt[k] == 0) zero_seen = 1;
        }
        /* cnt는 사이클 끝에 갱신되므로 한 사이클 통째로 돌린다 */
        for (int t = 0; t < TICKS_CYCLE; t++) tick_step();
    }
    printf("카운트 0 발생 여부: %s\n", zero_seen ? "발생함 (문제)" : "발생 안 함 (정상)");

    printf("\n=== 레지스터 계산(BSRR) 자체가 맞는지 확인: G1만 켠 경우 ===\n");
    {
        uint32_t r = drive_bsrr(0x001u); /* G1(PC0)만 켬 */
        printf("bits=0x001 -> BSRR=0x%08X\n", r);
        printf("  PC0 reset(Low=점등) 비트(bit16) = %s  (기대: 켜짐)\n",
               (r & (1u << 16)) ? "켜짐" : "꺼짐");
        printf("  PC1 set(High=소등) 비트(bit1)   = %s  (기대: 켜짐, 즉 꺼짐 상태)\n",
               (r & (1u << 1)) ? "켜짐" : "꺼짐");
    }

    return 0;
}
