#pragma once

#include <stdint.h>
#include <stdbool.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/osmo_io.h>

#include <osmo-bts/scheduler.h>
#include <osmo-bts/bts_trx.h>

#include <linux/if_packet.h>
#include "btsconfig.h"


struct virt_um_inst;

enum phy_link_type {
	PHY_LINK_T_NONE,
	PHY_LINK_T_SYSMOBTS,
	PHY_LINK_T_OSMOTRX,
	PHY_LINK_T_VIRTUAL,
};

enum phy_link_state {
	PHY_LINK_SHUTDOWN,
	PHY_LINK_CONNECTING,
	PHY_LINK_CONNECTED,
};

/* A PHY link represents the connection to a given PHYsical layer
 * implementation.  That PHY link contains 1...N PHY instances, one for
 * each TRX */
struct phy_link {
	struct llist_head list;
	int num;
	enum phy_link_type type;
	enum phy_link_state state;
	struct llist_head instances;
	char *description;
	union {
		struct {
		} sysmobts;
		struct {
			char *local_ip;
			char *remote_ip;
			uint16_t base_port_local;
			uint16_t base_port_remote;
			struct osmo_io_fd *trx_clk_iofd;
			uint32_t clock_advance;
			uint32_t rts_advance;
			bool use_legacy_setbsic;
			uint8_t trxd_pdu_ver_max; /* Maximum TRXD PDU version to negotiate */
			bool powered; /* last POWERON (true) or POWEROFF (false) confirmed */
			bool poweron_sent; /* is there a POWERON in transit? */
			bool poweroff_sent; /* is there a POWEROFF in transit? */
		} osmotrx;
		struct {
			char *mcast_dev;		/* Network device for multicast */
			int ttl;			/* TTL of transmitted udp multicast */
			char *bts_mcast_group;		/* BTS are listening to this group */
			uint16_t bts_mcast_port;
			char *ms_mcast_group;		/* MS are listening to this group */
			uint16_t ms_mcast_port;
			struct virt_um_inst *virt_um;
		} virt;
		struct {
			/* MAC address of the PHY */
			struct sockaddr_ll phy_addr;
			/* Network device name */
			char *netdev_name;

			/* configuration */
			uint32_t rf_port_index;
#if OCTPHY_USE_ANTENNA_ID == 1
			uint32_t rx_ant_id;
			uint32_t tx_ant_id;
#endif
			uint32_t rx_gain_db;
			bool tx_atten_flag;
			uint32_t tx_atten_db;
			bool over_sample_16x;
#if OCTPHY_MULTI_TRX == 1
			/* arfcn used by TRX with id 0 */
			uint16_t center_arfcn;
#endif

			struct octphy_hdl *hdl;
		} octphy;
	} u;
};

struct phy_instance {
	/* liked inside phy_link.linstances */
	struct llist_head list;
	int num;
	char *description;
	char version[MAX_VERSION_LENGTH];
	/* pointer to the PHY link to which we belong */
	struct phy_link *phy_link;

	/* back-pointer to the TRX to which we're associated */
	struct gsm_bts_trx *trx; /* NOTE: may be NULL! */

	union {
		struct {
			/* configuration */
			uint8_t clk_use_eeprom;
			uint32_t dsp_trace_f;
			int clk_cal;
			uint8_t clk_src;
			char *calib_path;

			struct femtol1_hdl *hdl;
		} sysmobts;
		struct {
			struct trx_l1h *hdl;
			struct trx_dl_burst_req br[TRX_NR_TS];
		} osmotrx;
		struct {
			/* logical transceiver number within one PHY */
			uint32_t trx_id;
			/* trx lock state variable */
			int trx_locked;
		} octphy;
		struct {
			/* configuration */
			uint32_t dsp_trace_f;
			char *calib_path;
			int minTxPower;
			int maxTxPower;
			struct lc15l1_hdl *hdl;
			uint8_t max_cell_size;		/* 0:166 qbits*/
			uint8_t diversity_mode;		/* 0: SISO A, 1: SISO B, 2: MRC */
			uint8_t pedestal_mode;		/* 0: unused TS is OFF, 1: unused TS is in minimum Tx power */
			uint8_t dsp_alive_period;	/* DSP alive timer period  */
			uint8_t tx_pwr_adj_mode;	/* 0: no auto adjust power, 1: auto adjust power using RMS detector */
			uint8_t tx_pwr_red_8psk;	/* 8-PSK maximum Tx power reduction level in dB */
			uint8_t tx_c0_idle_pwr_red;	/* C0 idle slot Tx power reduction level in dB */
		} lc15;
                struct {
                        /* configuration */
                        uint32_t dsp_trace_f;
                        char *calib_path;
                        int minTxPower;
                        int maxTxPower;
                        struct oc2gl1_hdl *hdl;
                        uint8_t max_cell_size;          /* 0:166 qbits*/
                        uint8_t pedestal_mode;          /* 0: unused TS is OFF, 1: unused TS is in minimum Tx power */
                        uint8_t dsp_alive_period;       /* DSP alive timer period  */
                        uint8_t tx_pwr_adj_mode;        /* 0: no auto adjust power, 1: auto adjust power using RMS detector */
                        uint8_t tx_pwr_red_8psk;        /* 8-PSK maximum Tx power reduction level in dB */
                        uint8_t tx_c0_idle_pwr_red;     /* C0 idle slot Tx power reduction level in dB */
                } oc2g;
	} u;
};

struct phy_link *phy_link_by_num(int num);
struct phy_link *phy_link_create(void *ctx, int num);
void phy_link_destroy(struct phy_link *plink);
const char *phy_link_name(const struct phy_link *plink);
void phy_link_state_set(struct phy_link *plink, enum phy_link_state state);
enum phy_link_state phy_link_state_get(struct phy_link *plink);
const char *phy_link_state_name(enum phy_link_state state);
int phy_links_open(void);

struct phy_instance *phy_instance_by_num(const struct phy_link *plink, int num);
struct phy_instance *phy_instance_create(struct phy_link *plink, int num);
void phy_instance_link_to_trx(struct phy_instance *pinst, struct gsm_bts_trx *trx);
void phy_instance_destroy(struct phy_instance *pinst);
const char *phy_instance_name(const struct phy_instance *pinst);

static inline struct phy_instance *trx_phy_instance(const struct gsm_bts_trx *trx)
{
	OSMO_ASSERT(trx);
	return trx->pinst;
}

int bts_model_phy_link_open(struct phy_link *plink);
int bts_model_phy_link_close(struct phy_link *plink);

#define LOGPPHL(plink, section, lvl, fmt, args...) LOGP(section, lvl, "%s: " fmt, phy_link_name(plink), ##args)
#define LOGPPHI(pinst, section, lvl, fmt, args...) LOGP(section, lvl, "%s: " fmt, phy_instance_name(pinst), ##args)
