#ifndef LIP_PTP_INTERNAL_H
#define LIP_PTP_INTERNAL_H

#include "ltimer.h"
#include "lip_ptp_proto_defs.h"
#include "bmca/lip_ptp_port_announce_defs.h"
#include "bmca/lip_ptp_bmca_state_sel_defs.h"
#include "sync/lip_ptp_port_sync_defs.h"
#include "sync/lip_ptp_site_sync_defs.h"
#include "sync/lip_ptp_local_clk_sync_defs.h"
#include "master/lip_ptp_master_sync_defs.h"
#include "gptpcap/lip_ptp_port_gptp_cap_defs.h"
#include "md/eth/pdelay/lip_ptp_pdelay_req_defs.h"
#include "md/eth/pdelay/lip_ptp_pdelay_resp_defs.h"
#include "md/eth/sync/lip_ptp_md_eth_sync_recv_defs.h"
#include "md/eth/sync/lip_ptp_md_eth_sync_send_defs.h"
#include "nbrate/lip_ptp_nb_rate_ratio_defs.h"
#include "lip/ll/lip_ll.h"
#include "lip_ptp.h"
#include <stdint.h>
#include <string.h>

#define LIP_PTP_PRINT_FNS_FMT            "%d.%09d"
#define LIP_PTP_PRINT_FNS_VAL(f)         (int32_t)((f) >> LIP_PTP_FNS_SHIFT),(uint32_t)(((f) & LIP_PTP_FNS_FRACT_MASK) * LIP_PTP_SEC_NS_MUL / LIP_PTP_FNS_FRACT_MAX)
#define LIP_PTP_PRINT_TSTMP_FMT          "%d.%09d"
#define LIP_PTP_PRINT_TSTMP_VAL(t)       (t).sec,(t).ns
#define LIP_PTP_PRINT_PORT_NUM(p)        (HTON16((p)->pkt_port_num_be))

typedef struct {
    int port_idx;
    uint32_t port_mask;    /* маска для указания при передаче, чтобы пакет посылался именно по выбранному порту */
    uint16_t port_num;     /* лог. номер порта  */
    uint16_t pkt_port_num_be; /* номер порта в port identity, big endian как в пакете */    
    int32_t delay_assymetry;
    int32_t egress_latency;  /* значение в нс, добавляемое к метке времени исходящего пакета для компенсации latency */
    int32_t ingress_latency; /* значение в нс, добавляемое к метке времени входящего пакета для компенсации latency (т.е. содержится -latency, т.к. вычитается) */


    t_lip_ptp_port_stats stats;

    t_lip_ptp_port_announce_ctx announce;
    t_lip_ptp_port_gptp_cap_ctx gptp_cap;
    t_lip_ptp_nb_rate_ratio_calc_ctx nb_rate_ratio;
    union {
        struct {
            t_lip_ptp_pdelay_req_ctx pdelay_req;
            t_lip_ptp_pdelay_resp_ctx pdelay_resp;
            t_lip_ptp_eth_sync_recv_ctx sync_recv;
            t_lip_ptp_eth_sync_send_ctx sync_send;
        } eth;
    } md;
} t_lip_ptp_port_ctx;

typedef struct {
    uint16_t clkid_suffix;
} t_lip_ptp_inst_cfg;


/* Структура общего состояния узла PTP */
typedef struct {
    t_lip_ptp_system_identity    sys_id; /* System Identity текущего узла для BMCA */
    t_lip_ptp_time_info          sys_time_info; /* информация о локальном источнике времени, не входящая в sys_id */
    t_lip_ptp_inst_cfg           cfg; /* пользовательские настройки */
    t_lip_ptp_gm_stats           gm_stats; /* статистика и информация о состоянии GM */
    t_lip_ptp_bcma_state_sel_ctx bmca; /* контекст машины состояния BMCA */
    t_lip_ptp_site_sync          sync; /* контекст машины состояния site_sync */
    t_lip_ptp_local_clk_ctx      local_clk; /* контекст с информацией о локальном клоке */
    t_lip_ptp_master_sync_ctx    master; /* контекст состояния машины передачи информации от времени текущего узла, если он GM */
    t_lip_ptp_port_ctx           ports[LIP_PORT_MAX_CNT]; /* состояние всех портов */
} t_lip_ptp_inst_ctx;

typedef enum {
    LIP_PTP_MSG_PREP_FLAG_CMLDS = 1 << 0
} t_ptp_msg_prepare_flags;

typedef struct {
    const t_lip_rx_frame *ll_frame;
    t_lip_ptp_tstmp tstmp;
    uint8_t tstmp_valid;
    uint8_t msg_type;
    uint8_t sdo_id_mj;
} t_lip_ptp_rx_frame;

static LINLINE bool lip_ptp_port_is_en(t_lip_ptp_port_ctx *p) {
    return p->stats.state != LIP_PTP_PORT_STATE_DISABLED;
}

static LINLINE bool lip_ptp_port_cmlds_mode_is_en(t_lip_ptp_port_ctx *p) {
    return p->stats.flags & LIP_PTP_PORT_STATS_FLAG_CMLDS;
}

static LINLINE void lip_ptp_port_cmlds_mode_clr(t_lip_ptp_port_ctx *p) {
    p->stats.flags &= ~LIP_PTP_PORT_STATS_FLAG_CMLDS;
}

static LINLINE void lip_ptp_port_cmlds_mode_set(t_lip_ptp_port_ctx *p) {
    p->stats.flags |= LIP_PTP_PORT_STATS_FLAG_CMLDS;
}

static LINLINE bool lip_ptp_gm_is_present(t_lip_ptp_inst_ctx *inst) {
    return inst->gm_stats.flags & LIP_PTP_GM_STATS_FLAG_PRESENT;
}







static  LINLINE void f_cpy_prio(t_lip_ptp_bmca_prio_vector *v1, const t_lip_ptp_bmca_prio_vector *v2) {
    memcpy(v1, v2, LIP_PTP_BMCA_PRIO_VECTOR_SIZE);
}

static LINLINE int f_cmp_prio(const t_lip_ptp_bmca_prio_vector *v1, const t_lip_ptp_bmca_prio_vector *v2) {
    return memcmp(v1, v2, LIP_PTP_BMCA_PRIO_VECTOR_SIZE);
}

static LINLINE int f_prio_is_eq(const t_lip_ptp_bmca_prio_vector *v1, const t_lip_ptp_bmca_prio_vector *v2) {
    return f_cmp_prio(v1, v2) == 0;
}

static LINLINE uint8_t *f_set_tlv_hdr(uint8_t *buf, uint16_t code, uint16_t len) {
    *buf++ = (code >> 8) & 0xFF;
    *buf++ = (code >> 0) & 0xFF;
    *buf++ = (len  >> 8) & 0xFF;
    *buf++ = (len  >> 0) & 0xFF;
    return buf;
}

static LINLINE uint8_t *f_set_tlv_org_id(uint8_t *buf, uint32_t orgId, uint32_t subType) {
    *buf++ = (orgId   >> 16) & 0xFF;
    *buf++ = (orgId   >>  8) & 0xFF;
    *buf++ = (orgId   >>  0) & 0xFF;
    *buf++ = (subType >> 16) & 0xFF;
    *buf++ = (subType >>  8) & 0xFF;
    *buf++ = (subType >>  0) & 0xFF;
    return buf;
}


typedef void (*t_lip_ptp_tlv_parce_cb)(void *ctx, uint16_t tlv_type, const uint8_t *data, uint16_t len);
typedef void (*t_lip_ptp_orgtlv_parce_cb)(void *ctx, uint32_t orgId, uint32_t subType, const uint8_t *data, uint16_t len);
/** Разбор TLV (опций) принятого сообщения. Проходит по всей области TLV сообщения и на
 *  каждый TLV вызывает функцию обратного вызова
 *
 *  @param[in] opt_ptr  указатель на начало области пакета, откуда начинаются TLV (за фиксированной частью)
 *  @param[in] opt_end  указатель на байт за последним байтом пакета
 *  @param[in] ctx      непрозрачный указатель на контекст, который будует передаваться в функции обратного вызова
 *  @param[in] tlv_cb   функции обратного вызова для обработки TLV. Может быть NULL, если используется только org_cb.
 *  @param[in] org_cb   Обработка organization-specific TLV. Если не NULL, то для всех типов organization-specific TLV
 *                      идет разбор Organization-ID и SubType и вызывается данный TLV с передачей этих параметров */
void lip_ptp_parse_tlv(const uint8_t *opt_ptr, const uint8_t *opt_end, void *ctx, t_lip_ptp_tlv_parce_cb tlv_cb, t_lip_ptp_orgtlv_parce_cb org_cb);




extern t_lip_ptp_inst_ctx g_lip_ptp_inst_ctx;
static LINLINE t_lip_ptp_inst_ctx *f_ptp_port_inst_ctx(t_lip_ptp_port_ctx *p) {
    /* получение контекста всего PTP Instance по порту. Сейчас всегда единственный
     * PTP instance и данная функция возвращает всегда гобальный указатель, но в будущем
     * может быть изменено если понадобится несколько доменов и т.п. */
    return &g_lip_ptp_inst_ctx;
}

void lip_ptp_set_log_interval_tmr(t_ltimer *tmr, int8_t logInterval, int cnt);
t_lip_tx_frame *lip_ptp_prepare_frame(t_lip_ptp_port_ctx *p, t_lip_ptp_msg_type msgtype, uint16_t msglen, uint16_t flags);
void lip_ptp_frame_flush(t_lip_tx_frame *frame, uint16_t msglen);
bool lip_ptp_check_port_identity(const t_lip_ptp_port_ctx *p, const void *pi);
bool lip_ptp_check_clock_identity(const t_lip_ptp_clock_identity *ci);

#endif // LIP_PTP_INTERNAL_H
