  
/* Includes -----------------------------------------------------------------*/

#include <zephyr/sys/byteorder.h>
#include <zephyr/irq.h>
#include <hal/nrf_radio.h>
#include <nrfx_timer.h>

#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/nrf_clock_control.h>

#include "hidBridge_common.h"
#include "debugLog.h"

/* Global variables ---------------------------------------------------------*/
#define RX_BUFF_SIZE    MAX(HID_REPORT_ABS_VAL_SIZE, HID_REPORT_BTN_EVT_SIZE) + COM_HEADER_SIZE
#define TX_BUFF_SIZE    MAX(HID_REPORT_ABS_VAL_SIZE, HID_REPORT_BTN_EVT_SIZE) + COM_HEADER_SIZE
#define BUFF_SIZE       HIDPP36_IN_OUT_REPORT_SIZE + COM_HEADER_SIZE

#define RADIO_CHANNEL   0
#define RADIO_MODE      NRF_RADIO_MODE_NRF_2MBIT

#define TIMER_INST_IDX  0

#define CHAN_TO_FREQ(_channel) (2400 + _channel)

#define SYNC_PACKET     0x33
#define RX_DELAY_US     400
#define TX_DELAY_US     400

#define LEN_OFFSET      0
#define DATA_OFFSET     1

/* Private constants --------------------------------------------------------*/

/* Private macros -----------------------------------------------------------*/

/* Private typedefs ---------------------------------------------------------*/

/* Private variables --------------------------------------------------------*/
static bool mb_radioModeTX = false;
static bool mb_dataToSend = false;
// Buffer for the radio TX packet
static uint8_t tx_packet[BUFF_SIZE] = {0};
static uint8_t tx_length = 0;
// Buffer for the radio RX packet.
static uint8_t rx_packet[BUFF_SIZE] = {0};
// Number of transmitted packets.
static uint32_t tx_packet_cnt = 0;
// Number of received packets with valid CRC.
static uint32_t rx_packet_cnt = 0;
// Total payload size 
static uint16_t total_payload_size;

static nrfx_timer_t m_timer_inst = NRFX_TIMER_INSTANCE(TIMER_INST_IDX);

/* Private prototypes -------------------------------------------------------*/
static void
button_cb (uint32_t button_state, uint32_t has_changed);

static void 
timer_handler (nrf_timer_event_t event_type, void * p_context);

static void 
clock_init (void);

void
radio_handler (const void *context);

static void 
radio_disable (void);

static void 
radio_enableTx (void);

static void 
radio_enableRx (void);

static void
startTimer (uint32_t us_delay);
/* Public functions ---------------------------------------------------------*/
int16_t
hidBridgeDevInt_rfCtrl_init (void)
{
  nrfx_err_t status;
  int ret;

  debugLog_subscribe(__FILE__, DEBUGLOG_LEVEL_Info);

  clock_init();

  irq_connect_dynamic(RADIO_IRQn, IRQ_PRIO_LOWEST, radio_handler, NULL, 0);
  irq_enable(RADIO_IRQn);

  // Radio config
  nrf_radio_modecnf0_set(NRF_RADIO, true, RADIO_MODECNF0_DTX_Center);
  nrf_radio_crc_configure(NRF_RADIO, RADIO_CRCCNF_LEN_Two, NRF_RADIO_CRC_ADDR_SKIP, 0);
  nrf_radio_txaddress_set(NRF_RADIO, 0);
  nrf_radio_rxaddresses_set(NRF_RADIO, 1);
  nrf_radio_prefix0_set(NRF_RADIO, 0xAB);
  nrf_radio_base0_set(NRF_RADIO, 0xABABABAB);
  nrf_radio_packet_conf_t packet_conf;
  uint16_t frequency;
  
  // Configure sync packet
  memset(&packet_conf, 0, sizeof(packet_conf));
  packet_conf.lflen = 8;
  packet_conf.maxlen = (sizeof(tx_packet) - 1);
  packet_conf.statlen = 0;
  packet_conf.balen = 4;
  packet_conf.big_endian = true;
  packet_conf.whiteen = true;
  packet_conf.plen = NRF_RADIO_PREAMBLE_LENGTH_16BIT;
  total_payload_size = 2 + (packet_conf.balen + 1) + 1 + packet_conf.maxlen;
  nrf_radio_packet_configure(NRF_RADIO, &packet_conf);

  nrf_radio_mode_set(NRF_RADIO, RADIO_MODE);
  nrf_radio_txpower_set(NRF_RADIO, RADIO_TXPOWER_TXPOWER_Pos8dBm);

  frequency = CHAN_TO_FREQ(RADIO_CHANNEL);
  nrf_radio_frequency_set(NRF_RADIO, frequency);
  
  radio_disable();
  radio_enableRx();

  dk_leds_init(); // TODO: remove only for test
  dk_buttons_init(button_cb);

  IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_TIMER_INST_GET(TIMER_INST_IDX)), IRQ_PRIO_LOWEST,
            NRFX_TIMER_INST_HANDLER_GET(TIMER_INST_IDX), 0, 0);

  uint32_t base_frequency = NRF_TIMER_BASE_FREQUENCY_GET(m_timer_inst.p_reg);
  nrfx_timer_config_t config = NRFX_TIMER_DEFAULT_CONFIG(base_frequency);
  config.bit_width = NRF_TIMER_BIT_WIDTH_32;
  config.p_context = "Some context";

  status = nrfx_timer_init(&m_timer_inst, &config, timer_handler);
  NRFX_ASSERT(status == NRFX_SUCCESS);
  
  nrfx_timer_clear(&m_timer_inst);

  // Success
  return 0;
}

void
hidBridgeDevInt_rfCtrl_sendData (uint8_t *p_data, uint16_t length)
{
  if ((mb_dataToSend == false) && (length < BUFF_SIZE))
  {
    tx_packet[LEN_OFFSET] = length;
    memcpy(&tx_packet[DATA_OFFSET], p_data, length);
    mb_dataToSend = true;
    // start transmission if radio already configure in tx mode
    if (true == mb_radioModeTX)
    {
      nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_START);      
    }
  }
  else 
  {
    log_error("RF interface busy or length to long");
  }
}

void
radio_handler (const void *context)
{
  const struct radio_test_config *config =
    (const struct radio_test_config *) context;

  if (nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_CRCOK)) {
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCOK);
    dk_set_led(0,false);
    if (false == mb_radioModeTX)
    {
      if (SYNC_PACKET == rx_packet[DATA_OFFSET])
      {
        startTimer (RX_DELAY_US);
        rx_packet_cnt++;
        if (rx_packet_cnt >= 1000)
        {
          log_info("SYNC 1s");
          rx_packet_cnt = 0;
        }
      }
      else
      {
        log_info("RX DATA");
      }
      dk_set_led(0,true);
    }
  }
  else if (nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_END)) 
  {
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_END);
    mb_dataToSend = false;
    if (true == mb_radioModeTX)
    {
      tx_packet_cnt++;
    }
  }
  else if (nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_READY)) 
  {
    nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_READY);
    dk_set_led(2,mb_radioModeTX);
  }
  else 
  {
    log_error("not handle event");
  }

}

/* Private functions --------------------------------------------------------*/
static void 
timer_handler (nrf_timer_event_t event_type, void * p_context)
{  
  if(event_type == NRF_TIMER_EVENT_COMPARE0)
  {
    char * p_msg = p_context;
    dk_set_led(1,(mb_radioModeTX));
    if (false == mb_radioModeTX)
    {
      startTimer (TX_DELAY_US);
      radio_disable();
      radio_enableTx();
      mb_radioModeTX = true;
    }
    else
    {
      radio_disable();
      radio_enableRx();          
      mb_radioModeTX = false;
    }
  }
}

static void 
clock_init (void)
{
  int err;
  int res;
  struct onoff_manager *clk_mgr;
  struct onoff_client clk_cli;

  clk_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
  if (!clk_mgr) {
    log_error("Unable to get the Clock manager");
    return;
  }

  sys_notify_init_spinwait(&clk_cli.notify);

  err = onoff_request(clk_mgr, &clk_cli);
  if (err < 0) {
    log_error("Clock request failed: %d", err);
    return;
  }

  do {
    err = sys_notify_fetch_result(&clk_cli.notify, &res);
    if (!err && res) {
      log_error("Clock could not be started: %d", res);
      return;
    }
  } while (err);

  log_info("Clock has started");
}


static void 
radio_disable (void)
{
  nrf_radio_shorts_set(NRF_RADIO, 0);
  nrf_radio_int_disable(NRF_RADIO, ~0);
  nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);

  nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
  while (!nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_DISABLED)) {
    /* Do nothing */ // TODO: not blocking fct !!! 
  }
  nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);

}

static void 
radio_enableTx (void)
{
  nrf_radio_packetptr_set(NRF_RADIO, tx_packet);

  // SHORT ready and start if data to send already configured
  if (mb_dataToSend == true)
  {
    nrf_radio_shorts_set(NRF_RADIO, NRF_RADIO_SHORT_READY_START_MASK);
  }
  
  nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_END_MASK);
  nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_READY_MASK);

  nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_TXEN);
}

static void 
radio_enableRx (void)
{
  nrf_radio_shorts_set(NRF_RADIO,
        NRF_RADIO_SHORT_READY_START_MASK |
        NRF_RADIO_SHORT_END_START_MASK);
  
  nrf_radio_packetptr_set(NRF_RADIO, rx_packet);
  
  nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_CRCOK_MASK);

  nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_READY_MASK);

  nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_RXEN);
}

static void
startTimer (uint32_t us_delay)
{
  uint32_t desired_ticks = nrfx_timer_us_to_ticks(&m_timer_inst, us_delay);

  nrfx_timer_clear(&m_timer_inst);

  nrfx_timer_extended_compare(&m_timer_inst, NRF_TIMER_CC_CHANNEL0, desired_ticks,
                              NRF_TIMER_SHORT_COMPARE0_STOP_MASK|NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
                        
  nrfx_timer_enable(&m_timer_inst);
}
