#ifndef LIP_PTP_H
#define LIP_PTP_H

#include "lip_ptp_defs.h"
#include "lip_ptp_proto_defs.h"
#include "lip/lip_defs.h"
#include <string.h>

/** Флаги с информацией о состоянии порта PTP */
typedef enum {
    LIP_PTP_PORT_STATS_FLAG_NB_PDELAY_CAP       = 1 << 0, /**< Признак, что обнаружен ближайший сосед и он поддерживает PDelay */
    LIP_PTP_PORT_STATS_FLAG_NB_GPTP             = 1 << 1, /**< Признак, что обнаружен ближайший сосед и он поддерживает gPTP */
    LIP_PTP_PORT_STATS_FLAG_CMLDS               = 1 << 2, /**< Признак, что используется CMLDS для определиния PDelay */
    LIP_PTP_PORT_STATS_FLAG_NB_RATE_RATIO_VALID = 1 << 3, /**< Признак, что вычисленно действительное значение nbRateRatio*/
    LIP_PTP_PORT_STATS_FLAG_LINK_DELAY_VALID    = 1 << 4, /**< Признак, что вычисленно действительное значение meanLinkDelay*/
} t_lip_ptp_port_stats_flags;


/** Состояние порта. Основано на таблице 10-2 из IEEE 802.1AS-2020, с дополнительными вспомогательными значениями */
typedef enum {
    LIP_PTP_PORT_STATE_DISABLED = 0, /**< Порт принудительно запрещен */
    LIP_PTP_PORT_STATE_INIT,         /**< Порт разрешен, но еще не выполнено определение его текущего состояния */
    LIP_PTP_PORT_STATE_PASSIVE,      /**< Пассивный порт. По нему выполняется только измерение PDelay, остальной обмен не выполняется */
    LIP_PTP_PORT_STATE_MASTER,       /**< Мастер. Порт передает SYNC соседу */
    LIP_PTP_PORT_STATE_SLAVE,        /**< Подчиненный. Порт принимает SYNC от соседа. */
} t_lip_ptp_port_state;

/* Структура, описывающая состояние PTP порта */
typedef struct {
    uint32_t flags; /**< Набор флагов из #t_lip_ptp_port_stats_flags */
    t_lip_ptp_port_state  state; /**< Текущий режим работы порта. Значение из #t_lip_ptp_port_state. Соответствует переменной selectedState из 10.2.4.20 IEEE 802.1AS-2020 */
    t_lip_fns meanLinkDelay; /**< Вычисленная задержка обмена с соседом, измеренная локальным клоком соседа */
    double   nbRateRatio;  /**< Вычисленное отношение частоты локального клока соседа к частоте локального клока самого узла */
} t_lip_ptp_port_stats;


/** Информация о текущем времени, котороя берется либо из Announce времени, либо системная (если сами GM) */
typedef struct {
    uint8_t     flags1;           /**< Набор флагов из #t_lip_ptp_msg_flags1 */
    uint8_t     timeSource;       /**< Тип источника времени. Значение из LIP_PTP_TIME_SRC_xxx */
    uint16_t    currentUtcOffset; /**< Количество секунд, на которое текущее время мастера в TAI опережает время UTC
                                       (действительно только при установке флага #LIP_PTP_MSGFLAG1_CUR_UTC_OFFS_VALID) */
} t_lip_ptp_time_info;

typedef enum {
    LIP_PTP_GM_STATS_FLAG_PRESENT       = 1 << 0, /**< Признак, что GM определен */
    LIP_PTP_GM_STATS_FLAG_REMOTE        = 1 << 1, /**< Признак, что GM является удаленным узлом. Если не установлен при флаге PRESENT, то сам узел и есть GM */
    LIP_PTP_GM_STATS_FLAG_RATE_VALID    = 1 << 2, /**< Прзнак действительности вычисленного значение rateRatio */
    LIP_PTP_GM_STATS_FLAG_SELF_GM       = 1 << 3, /**< Признак, что сам узел начал работать как GM */
} t_lip_ptp_gm_stats_flags;

typedef struct {
    uint32_t                        flags; /**< Флаги из #t_lip_ptp_gm_stats_flags */
    double                          rateRatio; /**< Отношение GM rateRatio к локальному клоку */
    const t_lip_ptp_system_identity *sys_id; /**< System-ID для текущего GM */
    /* указатель на информацию о источнике времени, не входящию в вектор.
     * Указывает либо на sys_time_info (если сами gm),
     * либо на ann_time_info порта в состоянии slave */
    const t_lip_ptp_time_info       *time_info;
} t_lip_ptp_gm_stats;

/* Установка значения Priority2 (участвует в выборе GM по BCMA) */
void lip_ptp_set_system_priority2(uint8_t prio);
/* Установка последний двух байт ClockID. Первые 6 байт определяются по MAC, последние два можно
 * изменить пользоателю данной функцией */
void lip_ptp_set_clkid_suffix(uint16_t clkid_suffix);
void lip_ptp_port_enable(int port_idx, uint16_t port_num);
void lip_ptp_port_set_latency(int port_idx, int32_t egress, int32_t ingress);
void lip_ptp_port_disable(int port_idx);
void lip_ptp_port_reconnect(uint8_t port_idx);


const t_lip_ptp_port_stats *lip_ptp_port_stats(uint8_t port_idx);
const t_lip_ptp_gm_stats *lip_ptp_gm_stats(void);
const t_lip_ptp_system_identity *lip_ptp_sys_id(void);


bool lip_ptp_get_local_time(t_lip_ptp_tstmp *tstmp);
bool lip_ptp_gm_time_valid(void);
void lip_ptp_tstmp_local_to_gm(const t_lip_ptp_tstmp *loc, t_lip_ptp_tstmp *gm);
void lip_ptp_tstmp_gm_to_local(const t_lip_ptp_tstmp *gm, t_lip_ptp_tstmp *loc);

static LINLINE bool lip_ptp_clock_id_is_equal(const t_lip_ptp_clock_identity *id1, const t_lip_ptp_clock_identity *id2) {
    return memcmp(id1, id2, LIP_PTP_CLOCK_IDENTITY_SIZE) == 0;
}

static LINLINE bool lip_ptp_port_id_is_equal(const t_lip_ptp_port_identity *p1, const t_lip_ptp_port_identity *p2) {
    return lip_ptp_clock_id_is_equal(&p1->clockIdentity, &p2->clockIdentity) && (p1->portNum == p2->portNum);
}


#endif // LIP_PTP_H
