/*
 * btn.c
 *
 * created Oct 13, 2024
 *
 * read and process buttons from sub MCU.
 * provide button handling support to application.
 *
 */

#include "btn.h"

#include "hw/ch32sub.h"
#include "user_config.h"



BtnSub btn[BTN_COUNT];

uint8_t btn_pushed;
uint8_t btn_held;



static uint8_t btn_map_upr[BTN_COUNT] = {0, 3, 1, 2, 4};  // bot left, top left, bot right, top right
static uint8_t btn_map_rot[BTN_COUNT] = {2, 1, 3, 0, 4};  // bot left, top left, bot right, top right



__HIGH_CODE
void btn_push_cb(uint8_t idx)
{
    if (!(btn_pushed & (1 << idx))) {
        btn_pushed |= (1 << idx);
        if (btn[idx].cb_push) btn[idx].cb_push(idx);
    }
}

__HIGH_CODE
void btn_hold_cb(uint8_t idx)
{
    btn_held |= (1 << idx);
    if (btn[idx].cb_hold) btn[idx].cb_hold(idx);
}

__HIGH_CODE
void btn_release_cb(uint8_t idx)
{
    btn_pushed &= ~(1 << idx);
    btn_held   &= ~(1 << idx);
    if (btn[idx].cb_release) btn[idx].cb_release(idx);
}


__HIGH_CODE
void btn_commit_hold()
{
    uint8_t i, x;
    uint8_t val[BTN_COUNT*2];

    uint8_t *map;

    if (sysflags & SYS_OLED_ROTATE_X) {
        map = btn_map_rot;
    } else {
        map = btn_map_upr;
    }

    x = 0;
    for (i = 0; i < BTN_COUNT; i++) {
        val[x + 0x00] = btn[map[i]].hold >> 8;
        val[x + 0x01] = btn[map[i]].hold & 0xff;
        x += 2;
    }

    ch32sub_write(REG_BTN1_HOLD_HI, val, sizeof(val));

    x = 0;
    for (i = 0; i < BTN_COUNT; i++) {
        val[x + 0x00] = btn[map[i]].repeat >> 8;
        val[x + 0x01] = btn[map[i]].repeat & 0xff;
        x += 2;
    }

    ch32sub_write(REG_BTN1_REPEAT_HI, val, sizeof(val));
}

int8_t btn_get_idx(uint8_t map_idx)
{
    uint8_t *map;

    if (map_idx >= BTN_COUNT) return -1;

    if (sysflags & SYS_OLED_ROTATE_X) {
        map = btn_map_rot;
    } else {
        map = btn_map_upr;
    }

    return map[map_idx] & 0x7f;
}


__HIGH_CODE
void btn_intr()
{
    uint8_t i;
    uint8_t btn_state[4]; // [9];
    uint8_t *map;

    if (sysflags & SYS_OLED_ROTATE_X) {
        map = btn_map_rot;
    } else {
        map = btn_map_upr;
    }

    // get button data.
    ch32sub_read(REG_BTN_PUSHED_LATCHED, btn_state, sizeof(btn_state));

    // clear button interrupt flag
    // this is actually handled by the main routine so not needed here
    // ch32sub_write_1b(REG_INTR_FLAGS_CLEAR, INT_BTN);

    // process callbacks for new events
    for (i = 0; i < BTN_COUNT; i++) {
        if (btn_state[1] & (1 << i)) {
            btn_push_cb(map[i]);
        }
        if (btn_state[2] & (1 << i)) {
            btn_hold_cb(map[i]);
        }
        if (btn_state[3] & (1 << i)) {
            btn_release_cb(map[i]);
        }
    }
}
