#include "lip/app/ptp/lip_ptp_cfg_defs.h"
#if LIP_PTP_ENABLE
#include "lip_ptp_site_sync.h"
#include "lip_ptp_port_sync.h"
#include "lip_ptp_local_clk_sync.h"
#include "lip/app/ptp/misc/lip_ptp_tstmp_ops.h"

void lip_ptp_site_sync_init(t_lip_ptp_inst_ctx *inst) {
    inst->sync.last_info.valid = false;
}

void lip_ptp_site_sync_gm_change(t_lip_ptp_inst_ctx *inst) {
    inst->sync.last_info.valid = false;
    inst->gm_stats.rateRatio = 1.;
    inst->gm_stats.flags &= ~LIP_PTP_GM_STATS_FLAG_RATE_VALID;
}


void lip_ptp_site_sync_pull(t_lip_ptp_inst_ctx *inst) {
    if (inst->sync.last_info.valid && inst->sync.last_info.port_ctx && ltimer_expired(&inst->sync.rx_tout_tmr)) {
        inst->sync.last_info.valid = false;
        lip_printf(LIP_LOG_MODULE_PTP, LIP_LOG_LVL_PROGRESS, 0, "lip ptp: port %d sync tout!\n", LIP_PTP_PRINT_PORT_NUM((t_lip_ptp_port_ctx *)inst->sync.last_info.port_ctx));
    }
}


void lip_ptp_site_sync_tx(t_lip_ptp_inst_ctx *inst, const t_lip_ptp_port_md_sync_info *txinfo) {
    if (inst->sync.last_info.valid && (inst->gm_stats.flags & LIP_PTP_GM_STATS_FLAG_RATE_VALID)) {
        for (int port_idx = 0; port_idx < LIP_PORT_MAX_CNT; ++port_idx) {
            t_lip_ptp_port_ctx *p = &g_lip_ptp_inst_ctx.ports[port_idx];
            if (p->stats.state == LIP_PTP_PORT_STATE_MASTER) {
                lip_ptp_prot_sync_tx(p, txinfo);
            }
        }
    }
}

void lip_ptp_site_sync_proc_rx(t_lip_ptp_inst_ctx *inst, t_lip_ptp_port_ctx *p, const t_lip_ptp_port_md_sync_info *rxinfo, const t_lip_ptp_tstmp *sync_rx_tstmp) {
    t_lip_ptp_site_sync *ctx = &inst->sync;
    /* получение rateRatio своего клока относительно GM, через rateRatio соседа с GM и своего rateRatio с соседом */

    bool nbRateValid = (p->stats.flags & LIP_PTP_PORT_STATS_FLAG_NB_RATE_RATIO_VALID) != 0;
    int8_t parentLogSyncInterval = rxinfo->logMessageInterval;
    t_lip_ptp_port_md_sync_info md_sync;
    memcpy(&md_sync, rxinfo, sizeof(t_lip_ptp_port_md_sync_info));
    ctx->last_info.port_ctx = p;
    ctx->last_info.valid = true;
    lip_ptp_set_log_interval_tmr(&ctx->rx_tout_tmr, parentLogSyncInterval, LIP_PTP_SYNC_RECV_INTERVALS_TOUT);

    /* до того, как мы вычислили отношение частот с соседом мы не можем вычислить полное
     * отношение частот с мастером и точно рассчитать все задержки, соответственно
     * до этого момента не обновляем свои данные о времени GM и не посылаем дальше SYNC,
     * чтобы не создавать разрыв синхронизации */
    if (nbRateValid) {
        double nbGmRateRatio = rxinfo->rateRatio;
        double gmRateRatio = nbGmRateRatio + (p->stats.nbRateRatio - 1);
        md_sync.rateRatio = gmRateRatio;
        inst->gm_stats.rateRatio = gmRateRatio;
        inst->gm_stats.flags |= LIP_PTP_GM_STATS_FLAG_RATE_VALID;


        t_lip_ptp_tstmp sync_rx_time;
        t_lip_ptp_tstmp sync_rx_local_time;
        /* ClockSlaveSync Figure 10-9 */
        /* Вычисление время приема SYNC в измерении GM.
         * К метки времени добавляем поле коррекции для получения времени отправления
         * у соседа во временной шкале GM и добавляем задержку передачи переведенную в временную шкалу GM */
        f_pkt_tstmp_to_host(&rxinfo->preciseOriginTimestamp, &sync_rx_time);
        t_lip_fns cor = rxinfo->fupCorrection + p->stats.meanLinkDelay * nbGmRateRatio;
        /** @todo add delay assymetry */
        lip_ptp_tstmp_add_fns(&sync_rx_time, cor);
        /* Формула в 10-9 - по сути обратный пересчет из upstreamTxTime в tstmp для Sync.
         * Для избежания лишних вычислений передаем явно. Возможно если для каких-то
         * сред это сделать будет нельзя, то при нулевом sync_rx_tstmp сделать пресчет */
        sync_rx_local_time = *sync_rx_tstmp;

        lip_ptp_local_clk_sync_update(inst, &sync_rx_local_time, &sync_rx_time);


        /* В данной реализации шлем sync остальным по принятому SYNC от мастера.
         * Дополнительную настройку интервала передачи SYNC не используется */
        lip_ptp_site_sync_tx(inst, &md_sync);
    } else if (inst->gm_stats.flags & LIP_PTP_GM_STATS_FLAG_RATE_VALID) {
        inst->gm_stats.rateRatio = 1.;
        inst->gm_stats.flags &= ~LIP_PTP_GM_STATS_FLAG_RATE_VALID;
    }
}

void lip_ptp_site_sync_send_master(t_lip_ptp_inst_ctx *inst, const t_lip_ptp_port_md_sync_info *txinfo) {
    t_lip_ptp_site_sync *sm = &inst->sync;
    sm->last_info.port_ctx = NULL;
    sm->last_info.valid = true;
    lip_ptp_site_sync_tx(inst, txinfo);
}


#endif



