/*
 * Created on: Jul 27, 2024
 *
 * generic button handler like I do on most of my projects
 */

#include <ch32v00x.h>

#include "btn.h"



struct Btn btn[BTN_COUNT] = {0};



void btn_init()
{
    uint8_t i;

    // configure GPIO (now handled as part of main GPIO init function)
    // BTN_PORT->BSHR   = (BTN1_PUPD << BTN1_PIN) | (BTN2_PUPD << BTN2_PIN);
    // BTN_PORT->CFGLR &= ~((0xf << (BTN1_PIN*4)) | ((0xf << (BTN2_PIN*4))));
    // BTN_PORT->CFGLR |=   (0x8 << (BTN1_PIN*4)) |  (0x8 << (BTN2_PIN*4));

    // configure default setup
    for (i = 0; i < BTN_COUNT; i++) {
        btn[i]._mask = BTN_RELEASE;
    }

    btn[0]._pintype = BTN1_PIN | BTN_ACTIVE_LO;
    btn[1]._pintype = BTN2_PIN;

    // check button, and ignore if held
    if (!(BTN_PORT->INDR & (1 << BTN1_PIN))) {
        btn[0]._mask |= BTN_IGNORE;
    }
}

void btn_poll()
{
    uint8_t i;
    uint8_t r;
    uint8_t ignore;

    uint8_t pushed;

    for (i = 0; i < BTN_COUNT; i++) {
        pushed = 0;

        ignore = btn[i]._mask & BTN_IGNORE;
        r = BTN_PORT->INDR & (1 << (btn[i]._pintype & BTN_PIN_MASK));

        // active low type buttons
        if (!r && (btn[i]._pintype & BTN_ACTIVE_LO)) pushed = 1;
        // active high type buttons
        if (r && !(btn[i]._pintype & BTN_ACTIVE_LO)) pushed = 1;

        if (pushed) {
            // hold counter
            if (btn[i]._count < 0xffff) btn[i]._count++;

            // pushed long enough?
            if (btn[i]._count < BTN_DEBOUNCE) continue;

            // first push?
            if (!(btn[i]._mask & BTN_PUSH)) {
                btn[i]._mask = BTN_PUSH | ignore;
                if (btn[i].cb_push && !ignore) {
                    btn[i].cb_push(i);
                    btn[i]._mask |= (BTN_PUSH << 4);
                }
            } else if (btn[i]._count >= btn[i].hold) {
                // held to count limit

                // if button is not repeatable, do not retrigger
                if ((btn[i]._mask & BTN_HOLD) && !btn[i].repeat) continue;

                btn[i]._mask |= BTN_HOLD;
                // call callback only if not in ignore state
                if (btn[i].cb_hold && !ignore) {
                    btn[i].cb_hold(i);
                    btn[i]._mask |= (BTN_HOLD << 4);
                }

                // apply repeat rate to count
                if (btn[i].repeat > btn[i]._count) {
                    btn[i]._count = 0;
                } else btn[i]._count -= btn[i].repeat;
            }
        } else {
            // is not pushed
            if (!(btn[i]._mask & BTN_RELEASE)) {
                // note: release will remove ignore status
                btn[i]._mask = BTN_RELEASE;
                btn[i]._count = 0;
                // call callback only if not in ignore state
                if (btn[i].cb_release && !ignore) {
                    btn[i].cb_release(i);
                    btn[i]._mask |= (BTN_RELEASE << 4);
                }
            }
        }
    }
}
