/*
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef ZEPHYR_SUBSYS_BLUETOOTH_MESH_SUBNET_H_
#define ZEPHYR_SUBSYS_BLUETOOTH_MESH_SUBNET_H_

#include <stdint.h>
#include "net/buf.h"
#include "mesh_kernel.h"
#include "mesh_util.h"

#define BT_MESH_NET_FLAG_KR       BIT(0)
#define BT_MESH_NET_FLAG_IVU      BIT(1)

#define BT_MESH_KR_NORMAL         0x00
#define BT_MESH_KR_PHASE_1        0x01
#define BT_MESH_KR_PHASE_2        0x02
#define BT_MESH_KR_PHASE_3        0x03

/** Which of the two subnet.keys should be used for sending. */
#define SUBNET_KEY_TX_IDX(sub) ((sub)->kr_phase == BT_MESH_KR_PHASE_2)

struct bt_mesh_net_rx;
enum bt_mesh_key_evt;

/** Network message encryption credentials */
struct bt_mesh_net_cred {
	uint8_t nid;                /* NID */
	struct bt_mesh_key enc;     /* EncKey */
	struct bt_mesh_key privacy; /* PrivacyKey */
};

struct bt_mesh_beacon {
	uint32_t sent;        /* Timestamp of last sent beacon */
	uint32_t recv;        /* Timestamp of last received beacon */
	uint8_t  last;       /* Number of beacons during last
				      * observation window
				      */
	uint8_t  cur;        /* Number of beacons observed during
				      * currently ongoing window.
				      */
	uint8_t  cache[8];   /* Cached last beacon auth value */
	uint8_t  auth[8];    /* Beacon Authentication Value */
};

struct bt_mesh_subnet_keys {
	bool valid;
	struct bt_mesh_key net;         /* NetKey */
	struct bt_mesh_net_cred msg;
	uint8_t net_id[8];              /* Network ID */
#if defined(CONFIG_BT_MESH_GATT_PROXY) || defined(CONFIG_BT_MESH_PROXY_CLIENT)
	struct bt_mesh_key identity;    /* IdentityKey */
#endif
	struct bt_mesh_key beacon;      /* BeaconKey */
	struct bt_mesh_key priv_beacon; /* PrivateBeaconKey */
};

#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
struct priv_beacon_ctx{
	uint16_t idx;        /* Private beacon random index */
	bool node_id;        /* Private Node Identity enabled */
	uint8_t data[5];     /* Private Beacon data */
};
#endif

/** Subnet instance. */
struct bt_mesh_subnet {
	uint16_t net_idx;            /* NetKeyIndex */

	uint8_t  kr_phase;           /* Key Refresh Phase */

	uint8_t  node_id;            /* Node Identity State */
	uint32_t node_id_start;      /* Node Identity started timestamp */

	struct bt_mesh_beacon secure_beacon;

#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
	struct bt_mesh_beacon priv_beacon;
	struct priv_beacon_ctx priv_beacon_ctx;
#endif

	struct bt_mesh_subnet_keys keys[2];
#if defined(CONFIG_BT_MESH_PROXY_SOLICITATION)
	bool sol_tx;
#endif
#if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV)
	uint32_t priv_net_id_sent; /* Timestamp for Private Network ID advertising
				    * started via Proxy Solicitation
				    */
	bool solicited;      /* Subnet received valid Solicitation PDU */
#endif
};

/** Subnet callback structure. Instantiate with @ref BT_MESH_SUBNET_CB */
struct bt_mesh_subnet_cb {
	void (*evt_handler)(struct bt_mesh_subnet *subnet,
			    enum bt_mesh_key_evt evt);
#if (CONFIG_MESH_CB_REGISTERED)
	struct bt_mesh_subnet_cb *next;
#endif
} __attribute__((aligned(4)));

/**
 *  @brief Register a subnet event callback.
 *
 *  @param _name Handler name.
 */
#if (CONFIG_MESH_CB_REGISTERED)
void bt_mesh_subnet_cb_register(struct bt_mesh_subnet_cb *p_subnet_cb);
#else
#define SUBNET_CB_SECTION __attribute__((section(".MESH_SUBNET_CBS"))) __attribute__((aligned(4))) __attribute__((used))
#define BT_MESH_SUBNET_CB_DEFINE(_name)                                    \
	static const struct bt_mesh_subnet_cb bt_mesh_subnet_cb_ ## _name SUBNET_CB_SECTION
#endif

/** @brief Reset all Network keys. */
void bt_mesh_net_keys_reset(void);

/** @brief Call cb on every valid Subnet until it returns a non-zero value.
 *
 *  @param cb Callback to call, or NULL to return first valid subnet. If the callback returns true,
 *            iteration stops, and the passed subnet is returned.
 *  @param cb_data Callback data to pass to callback.
 *
 *  @return Subnet that returned non-zero value.
 */
struct bt_mesh_subnet *bt_mesh_subnet_find(bool (*cb)(struct bt_mesh_subnet *sub, void *cb_data),
					   void *cb_data);

/** @brief Iterate through all valid Subnets.
 *
 *  @param cb Callback to call on every Subnet.
 *
 *  @returns The number of valid subnets.
 */
size_t bt_mesh_subnet_foreach(void (*cb)(struct bt_mesh_subnet *sub));

/** @brief Get the next valid Subnet.
 *
 *  If there's only one valid Subnet, this will be returned on every call.
 *
 *  @param sub Previous Subnet, or NULL to get the first valid.
 *
 *  @returns Gets the next valid Subnet after @c sub, or NULL if there are no
 *           valid Subnets.
 */
struct bt_mesh_subnet *bt_mesh_subnet_next(struct bt_mesh_subnet *sub);

/** @brief Get a pointer to the Subnet with the given index.
 *
 *  @param net_idx Network index to look for.
 *
 *  @returns Subnet with index @c net_idx, or NULL if no such Subnet is known.
 */
struct bt_mesh_subnet *bt_mesh_subnet_get(uint16_t net_idx);

/** @brief Initialize a new Subnet.
 *
 *  @param net_idx Network index of the Subnet.
 *  @param kr_phase Key refresh phase the Subnet should be in.
 *  @param key The current network key for the Subnet.
 *  @param new_key New network key, if available.
 *
 *  @returns 0 on success, or (negative) error code on failure.
 */
int bt_mesh_subnet_set(uint16_t net_idx, uint8_t kr_phase,
		       const struct bt_mesh_key *key, const struct bt_mesh_key *new_key);

/** @brief Create Friendship credentials.
 *
 *  @param cred Credential object to create.
 *  @param lpn_addr Address of the LPN node in the friendship.
 *  @param frnd_addr Address of the Friend node in the friendship.
 *  @param lpn_counter The LPN's counter parameter.
 *  @param frnd_counter The Friend node's counter parameter.
 *  @param key Network key to create the Friendship credentials for.
 *
 *  @returns 0 on success, or (negative) error code on failure.
 */
int bt_mesh_friend_cred_create(struct bt_mesh_net_cred *cred,
			       uint16_t lpn_addr, uint16_t frnd_addr,
			       uint16_t lpn_counter, uint16_t frnd_counter,
			       const struct bt_mesh_key *key);

/** @brief Destroy Friendship credentials.
 *
 *  @param cred Credential object to destroy.
 */
void bt_mesh_friend_cred_destroy(struct bt_mesh_net_cred *cred);

/** @brief Iterate through all valid network credentials to decrypt a message.
 *
 *  @param rx Network RX parameters, passed to the callback.
 *  @param in Input message buffer, passed to the callback.
 *  @param out Output message buffer, passed to the callback.
 *  @param cb Callback to call for each known network credential. Iteration
 *            stops when this callback returns @c true.
 *
 *  @returns Whether any of the credentials got a @c true return from the
 *           callback.
 */
bool bt_mesh_net_cred_find(struct bt_mesh_net_rx *rx, struct net_buf_simple *in,
			   struct net_buf_simple *out,
			   bool (*cb)(struct bt_mesh_net_rx *rx,
				      struct net_buf_simple *in,
				      struct net_buf_simple *out,
				      const struct bt_mesh_net_cred *cred));

/** @brief Get the network flags of the given Subnet.
 *
 *  @param sub Subnet to get the network flags of.
 *
 *  @returns A bitmap of @ref BT_MESH_NET_FLAG_KR and @ref BT_MESH_NET_FLAG_IVU.
 */
uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub);

/** @brief Process a Key Refresh event from a beacon.
 *
 *  @param sub Subnet the Key Refresh was received on.
 *  @param kr_flag Key Refresh flag.
 *  @param new_key Whether the Key Refresh event was received on the new key
 *                 set.
 *
 *  @returns Whether the Key Refresh event caused a change.
 */
void bt_mesh_kr_update(struct bt_mesh_subnet *sub, bool kr_flag, bool new_key);

/** @brief Check whether the Subnet has the refreshed keys.
 *
 *  @param sub Subnet.
 *
 *  @returns Whether the Subnet's second key is valid.
 */
static inline bool
bt_mesh_subnet_has_new_key(const struct bt_mesh_subnet *sub)
{
	return sub->kr_phase != BT_MESH_KR_NORMAL;
}

/** Kind of currently enabled Node Identity state on one or more subnets. */
enum bt_mesh_subnets_node_id_state {
	/* None node identity states are enabled on any subnets. */
	BT_MESH_SUBNETS_NODE_ID_STATE_NONE,
	/* Node Identity state is enabled on one or more subnets. */
	BT_MESH_SUBNETS_NODE_ID_STATE_ENABLED,
	/* Private Node Identity state is enabled on one or more subnets. */
	BT_MESH_SUBNETS_NODE_ID_STATE_ENABLED_PRIVATE,
};

/** @brief Returns what kind of node identity state is currently enabled on one or more subnets.
 *
 * Only one kind (either non-private or private) can be enabled at the same time on all subnets.
 *
 * @returns Kind of node identity state that is currently enabled.
 */
enum bt_mesh_subnets_node_id_state bt_mesh_subnets_node_id_state_get(void);

/** @brief Store the Subnet information in persistent storage.
 *
 * @param net_idx Network index to store.
 */
void bt_mesh_subnet_store(uint16_t net_idx);

/** @brief Store the pending Subnets in persistent storage. */
void bt_mesh_subnet_pending_store(void);

#if (CONFIG_MESH_CB_REGISTERED)
void bt_mesh_subnet_settings_init(void);

void bt_mesh_subnet_init(void);
#endif

#endif /* ZEPHYR_SUBSYS_BLUETOOTH_MESH_SUBNET_H_ */
