#include "app_config.h"
#include "app_types.h"

AppContext_t g_app =
{
    .role = APP_ROLE_DEFAULT,
    .zigbee_state = APP_ZB_DISCONNECTED,
    .slave_inputs =
    {
        .buttons =
        {
            { .pressed = false, .changed = false, .last_change_ms = 0U },
            { .pressed = false, .changed = false, .last_change_ms = 0U },
            { .pressed = false, .changed = false, .last_change_ms = 0U }
        },
        .analog = { .raw = 0U, .filtered = 0U, .percent = 0U },
        .sequence = 0U
    },
    .master_node =
    {
        .online = false,
        .last_report = { .sequence = 0U, .button_mask = 0U, .analog_raw = 0U, .analog_percent = 0U },
        .last_seen_ms = 0U
    },
    .zigbee =
    {
        .pan_id = APP_ZIGBEE_PAN_ID,
        .channel = APP_ZIGBEE_CHANNEL,
        .endpoint = APP_ZIGBEE_ENDPOINT,
        .cluster_id = APP_ZIGBEE_CLUSTER_INPUTS
    },
    .uptime_ms = 0U,
    .last_report_ms = 0U
};

volatile uint32_t watch_role = APP_ROLE_DEFAULT;
volatile uint32_t watch_is_master = 0U;
volatile uint32_t watch_is_slave = 1U;
volatile uint32_t watch_role_pin_is_master = 0U;
volatile uint32_t watch_zigbee_state = APP_ZB_DISCONNECTED;
volatile uint32_t watch_master_online = 0U;
volatile uint32_t watch_master_last_seen_ms = 0U;
volatile uint32_t watch_master_last_sequence = 0U;
volatile uint32_t watch_master_button_mask = 0U;
volatile uint32_t watch_master_analog_raw = 0U;
volatile uint32_t watch_master_analog_percent = 0U;
volatile uint32_t watch_slave_sequence = 0U;
volatile uint32_t watch_slave_button_mask = 0U;
volatile uint32_t watch_slave_analog_raw = 0U;
volatile uint32_t watch_slave_analog_percent = 0U;
volatile uint32_t watch_report_tx_count = 0U;
volatile uint32_t watch_report_rx_count = 0U;
volatile uint32_t watch_report_tx_attempt_count = 0U;
volatile uint32_t watch_report_tx_ok_count = 0U;
volatile uint32_t watch_report_tx_busy_count = 0U;
volatile uint32_t watch_report_tx_confirm_count = 0U;
volatile uint32_t watch_report_tx_confirm_status = 0U;
volatile uint32_t watch_report_rx_ind_count = 0U;
volatile uint32_t watch_report_rx_decode_fail_count = 0U;
volatile uint32_t watch_report_rx_last_cluster = 0U;
volatile uint32_t watch_report_rx_last_length = 0U;
volatile uint32_t watch_zigbee_ready = 0U;
volatile uint32_t watch_rejoin_request_count = 0U;
volatile uint32_t watch_rejoin_success_count = 0U;
volatile uint32_t watch_rejoin_fail_count = 0U;
volatile uint32_t watch_rejoin_active = 0U;
volatile uint32_t watch_rejoin_last_status = 0U;
volatile uint32_t watch_report_tx_fail_streak = 0U;
volatile uint32_t watch_permit_join_count = 0U;
volatile uint32_t watch_permit_join_status = 0U;
volatile uint32_t watch_permit_join_duration = 0U;
volatile uint32_t watch_join_watchdog_count = 0U;

static void App_DebugCopyReportToMasterWatch(const AppSlaveReport_t *report)
{
    watch_master_last_sequence = report->sequence;
    watch_master_button_mask = report->button_mask;
    watch_master_analog_raw = report->analog_raw;
    watch_master_analog_percent = report->analog_percent;
}

static void App_DebugCopyReportToSlaveWatch(const AppSlaveReport_t *report)
{
    watch_slave_sequence = report->sequence;
    watch_slave_button_mask = report->button_mask;
    watch_slave_analog_raw = report->analog_raw;
    watch_slave_analog_percent = report->analog_percent;
}

const char *App_RoleName(AppRole_t role)
{
    return (role == APP_ROLE_MASTER) ? "master" : "slave";
}

void App_SetRole(AppRole_t role)
{
    g_app.role = role;
    g_app.zigbee_state = APP_ZB_DISCONNECTED;
    g_app.last_report_ms = 0U;
}

AppSlaveReport_t App_MakeSlaveReport(const AppSlaveInputs_t *inputs)
{
    AppSlaveReport_t report;
    uint8_t mask = 0U;

    for (uint8_t i = 0U; i < APP_BUTTON_COUNT; i++)
    {
        if (inputs->buttons[i].pressed)
        {
            mask |= (uint8_t)(1U << i);
        }
    }

    report.sequence = inputs->sequence;
    report.button_mask = mask;
    report.analog_raw = inputs->analog.raw;
    report.analog_percent = inputs->analog.percent;

    return report;
}

void App_MasterAcceptReport(const AppSlaveReport_t *report, uint32_t now_ms)
{
    g_app.master_node.online = true;
    g_app.master_node.last_report = *report;
    g_app.master_node.last_seen_ms = now_ms;

    watch_report_rx_count++;
    App_DebugCopyReportToMasterWatch(report);
    App_DebugRefresh(watch_role_pin_is_master);
}

void App_DebugRefresh(uint32_t role_pin_is_master)
{
    watch_role = (uint32_t)g_app.role;
    watch_is_master = (g_app.role == APP_ROLE_MASTER) ? 1U : 0U;
    watch_is_slave = (g_app.role == APP_ROLE_SLAVE) ? 1U : 0U;
    watch_role_pin_is_master = role_pin_is_master;
    watch_zigbee_state = (uint32_t)g_app.zigbee_state;
    watch_master_online = g_app.master_node.online ? 1U : 0U;
    watch_master_last_seen_ms = g_app.master_node.last_seen_ms;
    App_DebugCopyReportToMasterWatch(&g_app.master_node.last_report);
}

void App_DebugOnSlaveReportTx(const AppSlaveReport_t *report)
{
    watch_report_tx_count++;
    App_DebugCopyReportToSlaveWatch(report);
}
