#include "lip/app/ptp/lip_ptp_cfg_defs.h"
#if LIP_PTP_ENABLE
#include "lip_ptp_md_eth_sync_recv.h"
#include "lip/app/ptp/lip_ptp_proto_defs.h"
#include "lip/app/ptp/lip_ptp_internal.h"
#include "lip/app/ptp/sync/lip_ptp_port_sync.h"
#include "lip/app/ptp/nbrate/lip_ptp_nb_rate_ratio.h"
#include "lip/app/ptp/misc/lip_ptp_tstmp_ops.h"
#include <string.h>

/* Обновление nbRateRatio на основе интервалов между SYN.
 * Используется только в случае, когда это нельзя сделать с помощью PDelayReq/Resp/FollowUp
 * (если удаленная строна на поддерживает возврат отдельно времен t2 и t3 */
static void f_update_rate_ratio(t_lip_ptp_port_ctx *p) {
    /* в случае, если PDelayResp/Fup не позволяет определить rate-ratio, используем sync */
    if (p->nb_rate_ratio.src == LIP_PTP_RATE_RATIO_SRC_SYNC) {
        t_lip_ptp_eth_sync_recv_ctx *ctx = &p->md.eth.sync_recv;
        t_ptp_nb_rate_ratio_tsmp rr_tstmp;
        f_pkt_tstmp_to_host(&ctx->rx_info.preciseOriginTimestamp, &rr_tstmp.t_rem);
        rr_tstmp.t_rem_cor = ctx->rx_info.fupCorrection;
        rr_tstmp.t_loc = ctx->rx_sync_tstmp;
        lip_ptp_nb_rate_ratio_calc(p, &rr_tstmp);
    }
}

/* Разбор опций FollowUp/Sync. Поиск опции FollowUpTLV и получение gmRateRatio и lastTimeChange */
static void f_parse_fup_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_FUP) {
            if (len >= (int)sizeof(t_lip_ptp_orgtlv_gptp_fup))  {
                const t_lip_ptp_orgtlv_gptp_fup *fup_tlv = (const t_lip_ptp_orgtlv_gptp_fup *)data;
                t_lip_ptp_port_md_sync_info *info = (t_lip_ptp_port_md_sync_info *)cb_ctx;
                int32_t rateOffs = NTOH32(fup_tlv->cumulativeScaledRateOffset);
                info->rateRatio = 1. + (double)rateOffs/(1ULL << 41); /* 11.4.4.3.6 cumulativeScaledRateOffset */
                lip_printf(LIP_LOG_MODULE_PTP, LIP_LOG_LVL_DEBUG_MEDIUM, 0,
                           "lip ptp: rx cummulative rate ratio: %.9f\n", info->rateRatio);
                memcpy(&info->lastChange, &fup_tlv->lastChange, sizeof(t_lip_ptp_last_time_change_info));
            }
        }
    }
}


static void f_parse_fup_opts(t_lip_ptp_port_ctx *p, t_lip_ptp_port_md_sync_info *info, const uint8_t *opt_ptr, const uint8_t *end_ptr) {
    info->rateRatio = 1.; /* На случай отсутствия FollowUp TLV инициализируем как 1. */
    lip_ptp_parse_tlv(opt_ptr, end_ptr, info, NULL, f_parse_fup_tlv);
}

void lip_ptp_md_eth_sync_recv_init(t_lip_ptp_port_ctx *p) {
    t_lip_ptp_eth_sync_recv_ctx *ctx = &p->md.eth.sync_recv;
    memset(ctx, 0, sizeof(t_lip_ptp_eth_sync_recv_ctx));
}

void lip_ptp_md_eth_sync_recv_pull(t_lip_ptp_port_ctx *p) {
    t_lip_ptp_eth_sync_recv_ctx *ctx = &p->md.eth.sync_recv;
    if (ctx->rx_wt_fup) {
        if (ltimer_expired(&ctx->rx_fup_tout_tmr)) {
            ctx->rx_wt_fup = false;
        }
    }
}

void lip_ptp_md_eth_sync_recv_proc_sync(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_sync)) && frame->tstmp_valid) {
        t_lip_ptp_eth_sync_recv_ctx *ctx = &p->md.eth.sync_recv;
        const t_lip_ptp_msg_sync *msg = (const t_lip_ptp_msg_sync *)pl->data;
        if (msg->hdr.domainNum == LIP_PTP_DOMAIN_NUM) {
            ctx->rx_sync_tstmp = frame->tstmp;

            /* 11.2.14.2.1 a) для one-step сохраняем corField из Sync, для twoStep дополнительно позже нужно добавить corFiled из fup */
            ctx->rx_info.fupCorrection = NTOH64(msg->hdr.corField);
            /* 11.2.14.2.1 b) */
            memcpy(&ctx->rx_src_port_id, &msg->hdr.srcPortIdentity, sizeof(t_lip_ptp_port_identity));
            /* 11.2.14.2.1 c) */
            ctx->rx_info.logMessageInterval = msg->hdr.logMsgInterval;

            /* 11.2.14.2.1 f) вычисление upstreamTxTime */
            ctx->rx_info.upstreamTxTime = ctx->rx_sync_tstmp;


            t_lip_fns delay = p->stats.meanLinkDelay/p->stats.nbRateRatio;
            if (!lip_ptp_port_cmlds_mode_is_en(p)) {
                /** @todo delay += delayAsymmetry/rateRatio */
            }
            lip_ptp_tstmp_add_fns(&ctx->rx_info.upstreamTxTime, -delay);
            lip_printf(LIP_LOG_MODULE_PTP, LIP_LOG_LVL_DEBUG_MEDIUM, 0,
                       "lip ptp: port %d sync rx time: " LIP_PTP_PRINT_TSTMP_FMT ", delay: " LIP_PTP_PRINT_FNS_FMT ", upstream tx time: " LIP_PTP_PRINT_TSTMP_FMT "\n",
                       LIP_PTP_PRINT_PORT_NUM(p), LIP_PTP_PRINT_TSTMP_VAL(frame->tstmp), LIP_PTP_PRINT_FNS_VAL(delay), LIP_PTP_PRINT_TSTMP_VAL(ctx->rx_info.upstreamTxTime));

            if (msg->hdr.flags0 & LIP_PTP_MSGFLAG0_TWO_STEP) {
                lip_ptp_set_log_interval_tmr(&ctx->rx_fup_tout_tmr, msg->hdr.logMsgInterval, 1);
                ctx->rx_wt_fup = true;
                ctx->rx_sync_seqid_be = msg->hdr.seqId;

            } else {
                /*11.2.14.2.1 d) для oneStep  preciseOriginTimestamp из sync */
                memcpy(&ctx->rx_info.preciseOriginTimestamp, &msg->originTstmp, LIP_PTP_PKT_TSTMP_SIZE);
                f_update_rate_ratio(p);
                f_parse_fup_opts(p, &ctx->rx_info, &pl->data[sizeof(t_lip_ptp_msg_sync)], &pl->data[pl->size]);
                lip_ptp_prot_sync_process_rx(p, &ctx->rx_info, &ctx->rx_sync_tstmp, &ctx->rx_src_port_id);
            }
        }
    }
}

void lip_ptp_md_eth_sync_recv_proc_fup(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_follow_up)) {
        t_lip_ptp_eth_sync_recv_ctx *ctx = &p->md.eth.sync_recv;
        const t_lip_ptp_msg_follow_up *msg = (const t_lip_ptp_msg_follow_up *)pl->data;
        if ((msg->hdr.domainNum == LIP_PTP_DOMAIN_NUM)
            && ctx->rx_wt_fup
            && (msg->hdr.seqId == ctx->rx_sync_seqid_be)) {

            /* 11.2.14.2.1 */
            /* a) followUpCorrectionField = sync.corField + fup.corField
             * (первое слагаемое сохраняется при обработке sync) */
            ctx->rx_info.fupCorrection += NTOH64(msg->hdr.corField);
            /*11.2.14.2.1 d) для twoStep  preciseOriginTimestamp из fup */
            memcpy(&ctx->rx_info.preciseOriginTimestamp, &msg->precOriginTstmp, LIP_PTP_PKT_TSTMP_SIZE);

            f_update_rate_ratio(p);

            /* 11.2.14.2.1 e),g),h, i) process follow up TLV */
            f_parse_fup_opts(p, &ctx->rx_info, &pl->data[sizeof(t_lip_ptp_msg_follow_up)], &pl->data[pl->size]);

            lip_ptp_prot_sync_process_rx(p, &ctx->rx_info, &ctx->rx_sync_tstmp, &ctx->rx_src_port_id);

            ctx->rx_wt_fup = false;
        }
    }
}




#endif


