// Copyright 2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.

#include <xs1.h>
#include <platform.h>
#include <stdbool.h>
#include <xcore/channel.h>
#include <xcore/hwtimer.h>
#include <xcore/port.h>
#include <xcore/select.h>
#include "button_handler.h"

#define BUTTON_0_MASK       (0x1)
#define PORT_BUTTON_BITS    (0x3)
#define BUTTON_0_PRESSED    (PORT_BUTTON_BITS & ~BUTTON_0_MASK)

// Define the port for the buttons.
// Initialising p_buttons with PORT_BUTTONS connects it through the XK-EVK-XU316
//   XN file with PORT_4D on tile[0].
// The datasheet for the XU316-1024-FB265 shows PORT_4D of tile[0] connected
//   through signals X0D16, X0D17, X0D18, and X0D19 to balls E4, E3, E1, and F2.
// The schematic for the XK-EVK-XU316 board shows that two of these signals,
//   X0D16 and X0D17, connect directly to Button 0 and Button 1 respectively.
static port_t p_buttons = PORT_BUTTONS;

void button_handler(chanend c_gpi)
{
    // Prepare the button port for input
    port_enable(p_buttons);
    int32_t button_state = port_in(p_buttons);
    port_set_trigger_in_equal(p_buttons, BUTTON_0_PRESSED);

    // Prepare a timer for handling bouncing by the button input
    hwtimer_t t_settle = hwtimer_alloc();
    const uint32_t SETTLE_TIME = 5000000; // 50ms in 10ns ticks

    const bool BUTTON_PRESSED = true;
    bool stable = true;

    SELECT_RES(
        CASE_GUARD_THEN(p_buttons, stable, event_button_port),
        CASE_NGUARD_THEN(t_settle, stable, event_button_settle_timer)
    ) // SELECT_RES
    {
        event_button_port:
            button_state = port_in(p_buttons);
            port_clear_trigger_in(p_buttons);
            chan_out_word(c_gpi, BUTTON_PRESSED);
            hwtimer_set_trigger_time(t_settle, hwtimer_get_time(t_settle) + SETTLE_TIME);
            stable = false;
            continue; // end of event_button_port

        event_button_settle_timer:
            button_state = port_in(p_buttons);

            // Active low; ignore values for buttons other than button 0
            if(0 == (button_state & BUTTON_0_MASK)) {
                hwtimer_change_trigger_time(t_settle, hwtimer_get_time(t_settle) + SETTLE_TIME);
            } else {
                hwtimer_clear_trigger_time(t_settle);
                stable = true;
                port_set_trigger_in_equal(p_buttons, BUTTON_0_PRESSED);
            }

            continue; // end of event_button_settle_timer
    } // end of select
}
