#include "lip/app/ptp/lip_ptp_cfg_defs.h"
#if LIP_PTP_ENABLE
#include "lip/app/ptp/lip_ptp_internal.h"
#include "lip/link/lip_port_link.h"
#include "lip_ptp_port_gptp_cap.h"

static const t_lip_ptp_port_identity f_bcast_id = {
    {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
    0xFFFF
};



static void f_parse_gptpcap_tlv(void *cb_ctx, uint32_t orgId, uint32_t subType, const uint8_t *data, uint16_t len) {
    if (orgId == LIP_PTP_ORGID_CODE_GPTP) {
        if (subType == LIP_PTP_ORG_GPTP_SUBTYPE_GPTPCAP) {
            t_lip_ptp_port_ctx *p = (t_lip_ptp_port_ctx *)cb_ctx;
            t_lip_ptp_port_gptp_cap_ctx *ctx = &p->gptp_cap;
            if (len >= sizeof(t_lip_ptp_orgtlv_gptp_cap))  {
                const t_lip_ptp_orgtlv_gptp_cap *cap = (const t_lip_ptp_orgtlv_gptp_cap *)data;
                lip_ptp_set_log_interval_tmr(&ctx->rcv_tout_tmr, cap->logGptpCapableMessageInterval, LIP_PTP_GPTCAP_RECV_INTERVALS_TOUT);
                if (!(p->stats.flags & LIP_PTP_PORT_STATS_FLAG_NB_GPTP)) {
                    p->stats.flags |= LIP_PTP_PORT_STATS_FLAG_NB_GPTP;
                    lip_printf(LIP_LOG_MODULE_PTP, LIP_LOG_LVL_PROGRESS, 0, "lip ptp: port %d gptp cap receive (logInterval %d, tout %d)!\n",
                               LIP_PTP_PRINT_PORT_NUM(p), cap->logGptpCapableMessageInterval, ctx->rcv_tout_tmr.interval);
                }

            }
        }
    }
}

/* Передача Announce по порту (порт должен быть в состоянии master) */
static void f_tx_gptp_cap(t_lip_ptp_port_ctx *p) {
    const uint16_t opt_len = LIP_PTP_TLV_ORG_HDR_SIZE + sizeof(t_lip_ptp_orgtlv_gptp_cap);
    const uint16_t tx_size = sizeof(t_lip_ptp_msg_signaling) + sizeof(t_lip_ptp_tlv_hdr) + opt_len;
    t_lip_ptp_port_gptp_cap_ctx *ctx = &p->gptp_cap;
    t_lip_tx_frame *frame = lip_ptp_prepare_frame(p, LIP_PTP_MSGTYPE_SIGNALING, tx_size, 0);
    if (frame) {
        t_lip_ptp_msg_signaling *msg = (t_lip_ptp_msg_signaling*)frame->buf.cur_ptr;
        memcpy(&msg->targetPortIdentity, &f_bcast_id, LIP_PTP_PORT_IDENTITY_SIZE);
        ++ctx->tx_seq_id;
        msg->hdr.seqId = HTON16(ctx->tx_seq_id);
        uint8_t *opt_ptr = &frame->buf.cur_ptr[sizeof(t_lip_ptp_msg_signaling)];
        opt_ptr = f_set_tlv_hdr(opt_ptr, LIP_PTP_TLV_CODE_ORG_EXT_NOT_PROP, opt_len);
        opt_ptr = f_set_tlv_org_id(opt_ptr, LIP_PTP_ORGID_CODE_GPTP, LIP_PTP_ORG_GPTP_SUBTYPE_GPTPCAP);
        t_lip_ptp_orgtlv_gptp_cap *cap = (t_lip_ptp_orgtlv_gptp_cap *)opt_ptr;
        cap->logGptpCapableMessageInterval = ctx->tx_log_interval;
        cap->flags = 0;
        cap->reserved = 0;
        lip_ptp_frame_flush(frame, tx_size);
    }
}

void lip_ptp_port_gptp_cap_init(t_lip_ptp_port_ctx *p) {
    t_lip_ptp_port_gptp_cap_ctx *sm = &p->gptp_cap;
    memset(sm, 0, sizeof(t_lip_ptp_port_gptp_cap_ctx));
    sm->tx_log_interval = LIP_PTP_GPTCAP_LOG_INTERVAL;
    lip_ptp_set_log_interval_tmr(&sm->tx_interval_tmr, sm->tx_log_interval, 1);
}

void lip_ptp_port_gptp_cap_down(t_lip_ptp_port_ctx *p) {
    p->stats.flags &= ~LIP_PTP_PORT_STATS_FLAG_NB_GPTP;
}

void lip_ptp_port_gptp_cap_pull(t_lip_ptp_port_ctx *p) {
    t_lip_ptp_port_gptp_cap_ctx *sm = &p->gptp_cap;
    if (p->stats.flags & LIP_PTP_PORT_STATS_FLAG_NB_GPTP) {
        if (ltimer_expired(&sm->rcv_tout_tmr)) {
            lip_printf(LIP_LOG_MODULE_PTP, LIP_LOG_LVL_PROGRESS, 0, "lip ptp: port %d gptp cap tout!\n", LIP_PTP_PRINT_PORT_NUM(p));
            p->stats.flags &= ~LIP_PTP_PORT_STATS_FLAG_NB_GPTP;
        }
    }

    if (ltimer_expired(&sm->tx_interval_tmr)) {
        f_tx_gptp_cap(p);
        ltimer_restart(&sm->tx_interval_tmr);
    }
}

void lip_ptp_port_proc_signaling(t_lip_ptp_port_ctx *p, t_lip_payload *pl, const t_lip_ptp_rx_frame *frame) {
    if (pl->size >= sizeof(t_lip_ptp_msg_signaling)) {
        t_lip_ptp_msg_signaling *msg = (t_lip_ptp_msg_signaling *)pl->data;
        t_lip_ptp_inst_ctx *inst = f_ptp_port_inst_ctx(p);
        /* проверка, что signaling message относится либо к широковещательному
         * адресу/порту, либо непосредственно к нам */
        if ((lip_ptp_clock_id_is_equal(&msg->targetPortIdentity.clockIdentity, &f_bcast_id.clockIdentity)
             || (lip_ptp_clock_id_is_equal(&msg->targetPortIdentity.clockIdentity, &inst->sys_id.clockIdentity)))
            && ((msg->targetPortIdentity.portNum == f_bcast_id.portNum) ||
                (msg->targetPortIdentity.portNum == p->pkt_port_num_be))) {
            lip_ptp_parse_tlv(&pl->data[sizeof(t_lip_ptp_msg_signaling)], &pl->data[pl->size], p, NULL, f_parse_gptpcap_tlv);
        }
    }
}


#endif

