#include "aiw_soc_ui_client.h"

#include <string.h>

bool aiw_soc_ui_client_init(aiw_soc_ui_client *client, uint64_t session_id) {
    if (client == NULL || session_id == 0u) return false;
    memset(client, 0, sizeof(*client));
    client->session_id = session_id;
    client->next_sequence = 1u;
    return true;
}

bool aiw_soc_ui_client_encode_session_open(const aiw_soc_ui_client *client,
                                          uint8_t *out, size_t capacity,
                                          size_t *written) {
    if (client == NULL) {
        if (written != NULL) *written = 0u;
        return false;
    }
    const aiw_ui_session_open open = {client->session_id};
    return aiw_encode_ui_session_open(&open, out, capacity, written);
}

bool aiw_soc_ui_client_accept_session_ack(aiw_soc_ui_client *client,
                                          const aiw_ui_ack *ack) {
    if (client == NULL || ack == NULL || ack->session_id != client->session_id ||
        ack->sequence != 0u ||
        (ack->status != AIW_UI_ACK_SESSION_OPENED &&
         ack->status != AIW_UI_ACK_SESSION_REJECTED)) return false;
    client->session_open = ack->status == AIW_UI_ACK_SESSION_OPENED;
    return true;
}

bool aiw_soc_ui_client_begin_command(aiw_soc_ui_client *client, uint8_t button,
                                     uint8_t *out, size_t capacity,
                                     size_t *written) {
    if (written != NULL) *written = 0u;
    if (client == NULL || !client->session_open || client->command_pending ||
        button > AIW_UI_COMMAND_MAX_BUTTON || client->next_sequence == 0u ||
        out == NULL || written == NULL) return false;
    const aiw_ui_command command = {client->session_id, client->next_sequence, button};
    if (!aiw_encode_ui_command(&command, out, capacity, written)) return false;
    client->pending_command = command;
    client->command_pending = true;
    ++client->next_sequence;
    if (client->next_sequence == 0u) client->next_sequence = 1u;
    return true;
}

bool aiw_soc_ui_client_retry_command(const aiw_soc_ui_client *client,
                                     uint8_t *out, size_t capacity,
                                     size_t *written) {
    if (written != NULL) *written = 0u;
    if (client == NULL || !client->session_open || !client->command_pending)
        return false;
    return aiw_encode_ui_command(&client->pending_command, out, capacity, written);
}

bool aiw_soc_ui_client_accept_command_ack(aiw_soc_ui_client *client,
                                          const aiw_ui_ack *ack) {
    if (client == NULL || ack == NULL || !client->command_pending ||
        ack->session_id != client->session_id ||
        ack->sequence != client->pending_command.sequence ||
        (unsigned)ack->status > (unsigned)AIW_UI_ACK_SEQUENCE_COLLISION) return false;
    if (ack->status == AIW_UI_ACK_BAD_SESSION) client->session_open = false;
    client->command_pending = false;
    memset(&client->pending_command, 0, sizeof(client->pending_command));
    return true;
}
