/*
 * L1 — protocol primitives ported verbatim from
 * test_Update/mcu_structured_test.c.
 *
 * Function bodies match the reference 1:1, except names are namespaced
 * with mcu_*. printf-style diagnostics from the reference are kept out
 * of this layer; transport / upgrade layers log via Android ALOG instead.
 */

#define _POSIX_C_SOURCE 200809L

#include "mcu_protocol.h"

#include <string.h>
#include <time.h>

uint64_t mcu_now_ms(void)
{
    struct timespec ts;

    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (uint64_t)ts.tv_sec * 1000ULL + (uint64_t)ts.tv_nsec / 1000000ULL;
}

void mcu_sleep_ms(int delay_ms)
{
    struct timespec ts;

    if (delay_ms <= 0) {
        return;
    }
    ts.tv_sec = delay_ms / 1000;
    ts.tv_nsec = (long)(delay_ms % 1000) * 1000000L;
    nanosleep(&ts, NULL);
}

uint8_t mcu_seq_next(uint8_t seq)
{
    seq = (uint8_t)(seq + 1U);
    if (seq == 0U) {
        seq = 1U;
    }
    return seq;
}

uint8_t mcu_calc_checksum(const uint8_t *data, size_t len)
{
    uint16_t sum = 0;
    size_t i;

    for (i = 0; i < len; ++i) {
        sum = (uint16_t)(sum + data[i]);
    }
    return (uint8_t)(~(sum & 0xFFU));
}

uint16_t mcu_crc16_ccitt_update(uint16_t crc, const uint8_t *data, size_t len)
{
    size_t i;

    for (i = 0U; i < len; ++i) {
        uint8_t bit;

        crc ^= (uint16_t)((uint16_t)data[i] << 8);
        for (bit = 0U; bit < 8U; ++bit) {
            if ((crc & 0x8000U) != 0U) {
                crc = (uint16_t)((crc << 1) ^ 0x1021U);
            } else {
                crc <<= 1;
            }
        }
    }
    return crc;
}

bool mcu_is_power_of_two_u16(uint16_t value)
{
    return value != 0U && (value & (uint16_t)(value - 1U)) == 0U;
}

bool mcu_fill_upgrade_project_bytes(const uint8_t project_bytes[5])
{
    if (project_bytes == NULL) {
        return false;
    }
    return true;
}

const char *mcu_frame_type_name(uint8_t ft)
{
    switch (ft) {
    case FT_SETUP:    return "SETUP";
    case FT_ACK:      return "ACK";
    case FT_SETUPACK: return "SETUPACK";
    case FT_CLOSE:    return "CLOSE";
    case FT_HB:       return "HB";
    case FT_DATA:     return "DATA";
    case FT_TRANSFER: return "TRANSFER";
    case FT_BOOT:     return "BOOT";
    default:          return "UNKNOWN";
    }
}

const char *mcu_upgrade_cmd_name(uint8_t cmd)
{
    switch (cmd) {
    case UPDATE_M2A_UPDATE_ACK:        return "UPDATE_ACK";
    case UPDATE_M2A_UPDATE_LEN:        return "UPDATE_LEN";
    case UPDATE_M2A_UPDATE_IND_AQUIRE: return "UPDATE_IND_AQUIRE";
    case UPDATE_A2M_UPDATE_REQ:        return "UPDATE_REQ";
    case UPDATE_A2M_UPDATE_BEGIN:      return "UPDATE_BEGIN";
    case UPDATE_A2M_UPDATE_DATA:       return "UPDATE_DATA";
    case UPDATE_A2M_UPDATE_END:        return "UPDATE_END";
    default:                           return "UNKNOWN";
    }
}

const char *mcu_upgrade_ack_name(uint8_t ack_type)
{
    switch (ack_type) {
    case ACK_UPDATE_NONE:             return "NONE";
    case ACK_UPDATE_REJECT:           return "REJECT";
    case ACK_UPDATE_REQUEST:          return "REQUEST";
    case ACK_UPDATE_SUCCESS:          return "SUCCESS";
    case ACK_UPDATE_FAIL:             return "FAIL";
    case ACK_UPDATE_RX_LAST_FRAME_OK: return "RX_LAST_FRAME_OK";
    default:                          return "UNKNOWN";
    }
}

const char *mcu_radio_band_name(uint8_t band)
{
    return ((band & 0x0FU) == 0U) ? "FM" : "AM";
}

const char *mcu_radio_seek_name(uint8_t seek_type)
{
    switch (seek_type & 0x0FU) {
    case 0x00U: return "STOP";
    case 0x01U: return "AMS";
    case 0x02U: return "FORWARD";
    case 0x03U: return "BACKWARD";
    case 0x04U: return "PTY";
    case 0x05U: return "TA";
    default:    return "UNKNOWN";
    }
}

size_t mcu_build_frame_bytes(uint8_t *out, size_t out_cap, uint8_t seq, uint8_t ft,
                             const uint8_t *payload, size_t payload_len)
{
    size_t total_len;

    /* 0xFF 0xAA + len(2) + seq + type + payload + checksum */
    total_len = payload_len + 7U;
    if (out_cap < total_len || payload_len > MCU_MAX_PAYLOAD_LEN) {
        return 0U;
    }

    out[0] = MCU_FRAME_HEAD_0;
    out[1] = MCU_FRAME_HEAD_1;
    out[2] = (uint8_t)((total_len >> 8) & 0xFFU);
    out[3] = (uint8_t)(total_len & 0xFFU);
    out[4] = seq;
    out[5] = ft;
    if (payload_len > 0U) {
        memcpy(out + 6, payload, payload_len);
    }
    out[6 + payload_len] = mcu_calc_checksum(out + 2, payload_len + 4U);
    return total_len;
}

bool mcu_decode_complete_frame(const uint8_t *raw, size_t raw_len, McuFrame *frame)
{
    size_t   payload_len;
    uint16_t declared_len;
    uint8_t  checksum;

    if (raw_len < 7U || raw_len > MCU_MAX_FRAME_LEN) {
        return false;
    }
    if (raw[0] != MCU_FRAME_HEAD_0 || raw[1] != MCU_FRAME_HEAD_1) {
        return false;
    }

    declared_len = (uint16_t)(((uint16_t)raw[2] << 8) | raw[3]);
    if ((size_t)declared_len != raw_len) {
        return false;
    }

    checksum = mcu_calc_checksum(raw + 2, raw_len - 3U);
    if (checksum != raw[raw_len - 1U]) {
        return false;
    }

    payload_len = raw_len - 7U;
    memset(frame, 0, sizeof(*frame));
    memcpy(frame->raw, raw, raw_len);
    frame->raw_len = raw_len;
    frame->seq = raw[4];
    frame->ft = raw[5];
    frame->payload_len = payload_len;
    if (payload_len > 0U) {
        memcpy(frame->payload, raw + 6, payload_len);
    }
    return true;
}

size_t mcu_parser_feed(FrameParser *parser, const uint8_t *data, size_t len,
                       FrameConsumer consumer, void *opaque)
{
    size_t consumed_frames = 0;
    size_t offset = 0;
    size_t keep;

    /* Oversized input: keep only the newest bytes. */
    if (len > sizeof(parser->buffer)) {
        data += len - sizeof(parser->buffer);
        len = sizeof(parser->buffer);
        parser->len = 0U;
    }

    if (parser->len + len > sizeof(parser->buffer)) {
        keep = parser->len;
        if (keep > 4U) {
            keep = 4U;
        }
        if (keep > 0U) {
            memmove(parser->buffer, parser->buffer + parser->len - keep, keep);
        }
        parser->len = keep;
    }

    memcpy(parser->buffer + parser->len, data, len);
    parser->len += len;

    while (offset + 7U <= parser->len) {
        size_t   frame_len;
        McuFrame frame;

        if (parser->buffer[offset] != MCU_FRAME_HEAD_0
            || parser->buffer[offset + 1U] != MCU_FRAME_HEAD_1) {
            ++offset;
            continue;
        }

        frame_len = (size_t)(((size_t)parser->buffer[offset + 2U] << 8)
                             | parser->buffer[offset + 3U]);
        if (frame_len < 7U || frame_len > MCU_MAX_FRAME_LEN) {
            ++offset;
            continue;
        }

        if (offset + frame_len > parser->len) {
            break;
        }

        if (!mcu_decode_complete_frame(parser->buffer + offset, frame_len, &frame)) {
            ++offset;
            continue;
        }

        if (!consumer(opaque, &frame)) {
            ++consumed_frames;
            offset += frame_len;
            break;
        }

        ++consumed_frames;
        offset += frame_len;
    }

    if (offset > 0U) {
        if (offset < parser->len) {
            memmove(parser->buffer, parser->buffer + offset, parser->len - offset);
            parser->len -= offset;
        } else {
            parser->len = 0U;
        }
    }

    return consumed_frames;
}

void mcu_protocol_state_reset(ProtocolState *state)
{
    state->link_state = LINK_IDLE;
    state->recv_seq = 1U;
    state->send_seq = 1U;
}

ProtocolOutcome mcu_protocol_on_frame(ProtocolState *state, const McuFrame *frame)
{
    ProtocolOutcome outcome;

    memset(&outcome, 0, sizeof(outcome));

    switch (frame->ft) {
    case FT_ACK:
        outcome.ack_seq = frame->seq;
        if (frame->seq == state->send_seq) {
            outcome.matched_ack = true;
            state->send_seq = mcu_seq_next(state->send_seq);
        }
        break;

    case FT_SETUPACK:
        outcome.got_setupack = true;
        outcome.setupack_seq = frame->seq;
        outcome.send_ack = true;
        outcome.link_up = true;
        state->recv_seq = frame->seq;
        state->send_seq = mcu_seq_next(frame->seq);
        state->link_state = LINK_LINKED;
        break;

    case FT_HB:
        if (state->link_state != LINK_LINKED) {
            break;
        }
        outcome.duplicate_rx = (state->recv_seq == frame->seq);
        state->recv_seq = frame->seq;
        /* HB response is also FT_HB[0x05]; the transport layer handles it. */
        break;

    case FT_DATA:
    case FT_TRANSFER:
    case FT_BOOT:
        if (state->link_state != LINK_LINKED) {
            break;
        }
        outcome.duplicate_rx = (state->recv_seq == frame->seq);
        state->recv_seq = frame->seq;
        outcome.send_ack = true;
        break;

    case FT_CLOSE:
        state->link_state = LINK_IDLE;
        outcome.link_down = true;
        break;

    default:
        break;
    }

    return outcome;
}

size_t mcu_build_upgrade_request_payload(uint8_t *payload, size_t capacity,
                                         const uint8_t project_bytes[5], uint32_t bin_size)
{
    if (capacity < 12U || !mcu_fill_upgrade_project_bytes(project_bytes)) {
        return 0U;
    }

    payload[0] = MT_UPDATE;
    payload[1] = UPDATE_A2M_UPDATE_REQ;
    payload[2] = UPDATE_STATE_MCU_UPGRADE_REQ;
    memcpy(payload + 3U, project_bytes, 5U);
    payload[8]  = (uint8_t)((bin_size >> 24) & 0xFFU);
    payload[9]  = (uint8_t)((bin_size >> 16) & 0xFFU);
    payload[10] = (uint8_t)((bin_size >> 8) & 0xFFU);
    payload[11] = (uint8_t)(bin_size & 0xFFU);
    return 12U;
}

size_t mcu_build_upgrade_begin_payload(uint8_t *payload, size_t capacity,
                                       const uint8_t project_bytes[5], uint32_t bin_size)
{
    if (capacity < 11U || !mcu_fill_upgrade_project_bytes(project_bytes)) {
        return 0U;
    }

    payload[0] = MT_UPDATE;
    payload[1] = UPDATE_A2M_UPDATE_BEGIN;
    payload[2] = (uint8_t)((bin_size >> 24) & 0xFFU);
    payload[3] = (uint8_t)((bin_size >> 16) & 0xFFU);
    payload[4] = (uint8_t)((bin_size >> 8) & 0xFFU);
    payload[5] = (uint8_t)(bin_size & 0xFFU);
    memcpy(payload + 6U, project_bytes, 5U);
    return 11U;
}

size_t mcu_build_upgrade_data_payload(uint8_t *payload, size_t capacity,
                                      uint16_t index, const uint8_t *chunk, size_t chunk_len)
{
    if (capacity < chunk_len + 4U) {
        return 0U;
    }

    payload[0] = MT_UPDATE;
    payload[1] = UPDATE_A2M_UPDATE_DATA;
    payload[2] = (uint8_t)((index >> 8) & 0xFFU);
    payload[3] = (uint8_t)(index & 0xFFU);
    if (chunk_len > 0U) {
        memcpy(payload + 4U, chunk, chunk_len);
    }
    return chunk_len + 4U;
}

size_t mcu_build_upgrade_end_payload(uint8_t *payload, size_t capacity, uint16_t crc16)
{
    if (capacity < 4U) {
        return 0U;
    }

    payload[0] = MT_UPDATE;
    payload[1] = UPDATE_A2M_UPDATE_END;
    payload[2] = (uint8_t)((crc16 >> 8) & 0xFFU);
    payload[3] = (uint8_t)(crc16 & 0xFFU);
    return 4U;
}
