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

#include <zephyr/types.h>
#include <zephyr.h>
#include <sys/byteorder.h>
#include <sys/printk.h>

#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include <bluetooth/bluetooth.h>
#include <dk_buttons_and_leds.h>
#include <device.h>
#include <soc.h>
#include <dk_buttons_and_leds.h>
#include <drivers/uart.h>
#include <drivers/i2c.h>
#include <nrfx_twi.h>
#include <nrfx_twis.h>
#include <devicetree.h>
#include <stdio.h>
#include <irq.h>
#include <settings/settings.h>

#define twi	DT_LABEL(DT_ALIAS(i2c0))
uint8_t txBufferI2C[96];

#define SLAVE_ADDR (0x70 >> 1U)
#define DT_NORDIC_NRF_TWIS_I2C_0_IRQ_0 3
#define DT_NORDIC_NRF_TWIS_I2C_0_IRQ_0_PRIORITY 0

typedef nrfx_twis_t nrf_drv_twis_t;
static const nrfx_twis_t m_twis = NRFX_TWIS_INSTANCE(0);
const struct device *i2c_dev;

#define TIMEOUT_SYNC_CREATE K_SECONDS(10)
#define NAME_LEN            30

static bool         per_adv_found;
static bt_addr_le_t per_addr;
static uint8_t      per_sid;

static K_SEM_DEFINE(sem_per_adv, 0, 1);
static K_SEM_DEFINE(sem_per_sync, 0, 1);
static K_SEM_DEFINE(sem_per_sync_lost, 0, 1);

/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)

#if DT_NODE_HAS_STATUS(LED0_NODE, okay)
#define HAS_LED     0
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
#define BLINK_ONOFF K_MSEC(500)

static struct k_work_delayable blink_work;
static bool                  led_is_on;

static void blink_timeout(struct k_work *work)
{
	led_is_on = !led_is_on;
	gpio_pin_set(led.port, led.pin, (int)led_is_on);

	k_work_schedule(&blink_work, BLINK_ONOFF);
}
#endif

static bool data_cb(struct bt_data *data, void *user_data)
{
	char *name = user_data;
	uint8_t len;

        printk("\ndata_cb = %x len=%d",data->type,data->data_len);
        memcpy(&txBufferI2C,&(data->type), 4); //add just to test so remove it
        
	switch (data->type) 
        {
            case BT_DATA_NAME_SHORTENED:
            case BT_DATA_NAME_COMPLETE:
      		len = MIN(data->data_len, NAME_LEN - 1);
      		memcpy(name, data->data, len);
      		name[len] = '\0';
      		return false;
            
            case BT_DATA_MANUFACTURER_DATA:
                   //printk("AAAAAAAAAAAAAAAAAAAAGGGG %d %s\n", data->data_len, data->data);
                   return true;

      	    default:
      		return true;
	}
}

static const char *phy2str(uint8_t phy)
{
	switch (phy) {
	case 0: return "No packets";
	case BT_GAP_LE_PHY_1M: return "LE 1M";
	case BT_GAP_LE_PHY_2M: return "LE 2M";
	case BT_GAP_LE_PHY_CODED: return "LE Coded";
	default: return "Unknown";
	}
}

static void scan_recv(const struct bt_le_scan_recv_info *info,
		      struct net_buf_simple *buf)
{
	char le_addr[BT_ADDR_LE_STR_LEN];
	char name[NAME_LEN];

	(void)memset(name, 0, sizeof(name));
       
printk("\nscan_recv with len=%d, size=%d ",buf->len, buf->size);
	bt_data_parse(buf, data_cb, name);

	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
        if (info->adv_type == 5)
          printk("\n[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i %s C:%u S:%u D:%u SR:%u E:%u Prim: %s, Secn: %s, Interval: 0x%04x (%u ms), SID: %u \n",
	       le_addr, info->adv_type, info->tx_power, info->rssi, name,
	       (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0,
	       (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0,
	       (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0,
	       (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0,
	       (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0,
	       phy2str(info->primary_phy), phy2str(info->secondary_phy),
	       info->interval, info->interval * 5 / 4, info->sid);

	if (!per_adv_found && info->interval) {
		per_adv_found = true;

		per_sid = info->sid;
		bt_addr_le_copy(&per_addr, info->addr);

		k_sem_give(&sem_per_adv);
	}
}

static struct bt_le_scan_cb scan_callbacks = {
	.recv = scan_recv,
};

static void sync_cb(struct bt_le_per_adv_sync *sync,
		    struct bt_le_per_adv_sync_synced_info *info)
{
	char le_addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));

	printk("\nsync_cb: PER_ADV_SYNC[%u]: [DEVICE]: %s synced, "
	       "Interval 0x%04x (%u ms), PHY %s\n",
	       bt_le_per_adv_sync_get_index(sync), le_addr,
	       info->interval, info->interval * 5 / 4, phy2str(info->phy));

	k_sem_give(&sem_per_sync);
}

static void term_cb(struct bt_le_per_adv_sync *sync,
		    const struct bt_le_per_adv_sync_term_info *info)
{
	char le_addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));

	printk("\nterm_cb: PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n",
	       bt_le_per_adv_sync_get_index(sync), le_addr);

	k_sem_give(&sem_per_sync_lost);
}

static void recv_cb(struct bt_le_per_adv_sync *sync,
		    const struct bt_le_per_adv_sync_recv_info *info,
		    struct net_buf_simple *buf)
{
	char le_addr[BT_ADDR_LE_STR_LEN];
	//char data_str[255];

	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
        if (buf->data[1] == BT_DATA_ASHISH)
        {
            //bin2hex(buf->data, buf->len, data_str, sizeof(data_str));
            memcpy(&txBufferI2C,&(buf->data[2]), (buf->len)-1);

            printk("\n recv_cb: PER_ADV_SYNC[%u]: [DEVICE]: %s, tx_power %i, "
	       "RSSI %i, CTE %u, data length %u, data-type=%x \n",
	       bt_le_per_adv_sync_get_index(sync), le_addr, info->tx_power,
	       info->rssi, info->cte_type, buf->len,buf->data[1]);
            printk("\n msg_data %x %x %x %x %x %x %x %x %x %x",buf->data[2],buf->data[3],buf->data[4],buf->data[5],buf->data[6],buf->data[7],buf->data[8],buf->data[9],buf->data[10],buf->data[11]);
        }
        else
        {
            printk("\n recv_cb: PER_ADV_SYNC[%u]: [DEVICE]: %s, tx_power %i, "
	       "RSSI %i, CTE %u, data length %u, data-type=%x \n",
	       bt_le_per_adv_sync_get_index(sync), le_addr, info->tx_power,
	       info->rssi, info->cte_type, buf->len,buf->data[1]);
        }
}
static struct bt_le_per_adv_sync_cb sync_callbacks = {
	.synced = sync_cb,
	.term = term_cb,
	.recv = recv_cb
};

static void twis_event_handler(nrfx_twis_evt_t const * const p_event)
{
printk("twis_event_handler\n");
    switch (p_event->type)
    {
        case NRFX_TWIS_EVT_READ_REQ:
        printk("NRFX_TWIS_EVT_READ_REQ\n");
            if (p_event->data.buf_req)
            {
               (void)nrfx_twis_tx_prepare((nrfx_twis_t const *)&m_twis, &txBufferI2C[0], sizeof(txBufferI2C));
            }
            break;
        case NRFX_TWIS_EVT_READ_DONE:
        printk("NRFX_TWIS_EVT_READ_DONE\n");
// always keep last message in buffer, in case off I2C crc failure, on next I2C poll same data transmitted 
            break;
        case NRFX_TWIS_EVT_WRITE_REQ:
            printk("NRFX_TWIS_EVT_WRITE_REQ\n");
            break;
        case NRFX_TWIS_EVT_WRITE_DONE:
             printk("NRFX_TWIS_EVT_WRITE_DONE\n");
            break;

        case NRFX_TWIS_EVT_READ_ERROR:
        case NRFX_TWIS_EVT_WRITE_ERROR:
        case NRFX_TWIS_EVT_GENERAL_ERROR:
            printk("NRFX_TWIS_EVT_GENERAL_ERROR\n");
            break;
        default:
 printk("DEFAULT\n");
            break;
    }
}

int I2CSlave_init(void)
{
  int err;

  IRQ_CONNECT(DT_NORDIC_NRF_TWIS_I2C_0_IRQ_0, DT_NORDIC_NRF_TWIS_I2C_0_IRQ_0_PRIORITY, 
              nrfx_isr, nrfx_twis_0_irq_handler, 0);
 
  nrfx_twis_config_t config;
  config.addr[0] = SLAVE_ADDR;
  config.addr[1] = 0;
  config.scl = 27;
  config.scl_pull = NRF_GPIO_PIN_NOPULL;
  config.sda = 26;
  config.sda_pull = NRF_GPIO_PIN_NOPULL;
  config.interrupt_priority = 0; // Highest

  err = nrfx_twis_init(&m_twis, &config, twis_event_handler);
  nrfx_twis_enable(&m_twis);
  return err;
}


void main(void)
{
	struct bt_le_per_adv_sync_param sync_create_param;
	struct bt_le_per_adv_sync *sync;
	int err;

	printk("\nStarting Periodic Advertising Synchronization Demo\n");

        /*i2c_dev = device_get_binding(DT_LABEL(DT_NODELABEL(i2c0)));
	if (!i2c_dev) {
		printk("I2C: Device driver not found.\n");
		//return;
	}*/


#if (HAS_LED)
	printk("\nChecking LED device...");
	if (!device_is_ready(led.port)) {
		printk("\nfailed.\n");
		return;
	}
	printk("\ndone.\n");

	printk("\nConfiguring GPIO pin...");
	err = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
	if (err) {
		printk("\nfailed.\n");
		return;
	}
	printk("\ndone.\n");

	k_work_init_delayable(&blink_work, blink_timeout);
#endif /* HAS_LED */

        err = I2CSlave_init();
	if (err != NRFX_SUCCESS) {
             printk("I2C init failed (err %d)\n", err);
             return;
	}

	/* Initialize the Bluetooth Subsystem */
	err = bt_enable(NULL);
	if (err) {
		printk("\nBluetooth init failed (err %d)\n", err);
		return;
	}

	printk("\nScan callbacks register...");
	bt_le_scan_cb_register(&scan_callbacks);
	printk("\nsuccess.\n");

	printk("\nPeriodic Advertising callbacks register...");
	bt_le_per_adv_sync_cb_register(&sync_callbacks);
	printk("\nSuccess.\n");




	printk("\nStart scanning...");
	err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
	if (err) {
		printk("\nfailed (err %d)\n", err);
		return;
	}
	printk("\nsuccess.\n");

	do {
#if (HAS_LED)
		struct k_work_sync work_sync;

		printk("\nStart blinking LED...\n");
		led_is_on = false;
		gpio_pin_set(led.port, led.pin, (int)led_is_on);
		k_work_schedule(&blink_work, BLINK_ONOFF);
#endif /* HAS_LED */

		printk("\nWaiting for periodic advertising...\n");
		per_adv_found = false;
		err = k_sem_take(&sem_per_adv, K_FOREVER);
		if (err) {
			printk("\nfailed (err %d)\n", err);
			return;
		}
		printk("\nFound periodic advertising.\n");

		printk("\nCreating Periodic Advertising Sync...");
		bt_addr_le_copy(&sync_create_param.addr, &per_addr);
		sync_create_param.options = 0;
		sync_create_param.sid = per_sid;
		sync_create_param.skip = 0;
		sync_create_param.timeout = 0xa;
		err = bt_le_per_adv_sync_create(&sync_create_param, &sync);
		if (err) {
			printk("\nfailed (err %d)\n", err);
			return;
		}
		printk("\nsuccess.\n");

		printk("\nWaiting for periodic sync...\n");
		err = k_sem_take(&sem_per_sync, TIMEOUT_SYNC_CREATE);
		if (err) {
			printk("\nfailed (err %d)\n", err);

			printk("\nDeleting Periodic Advertising Sync...");
			err = bt_le_per_adv_sync_delete(sync);
			if (err) {
				printk("\nfailed (err %d)\n", err);
				return;
			}
			continue;
		}
		printk("\nPeriodic sync established.\n");

#if (HAS_LED)
		printk("\nStop blinking LED.\n");
		k_work_cancel_delayable_sync(&blink_work, &work_sync);

		/* Keep LED on */
		led_is_on = true;
		gpio_pin_set(led.port, led.pin, (int)led_is_on);
#endif /* HAS_LED */

		printk("\nWaiting for periodic sync lost...\n");
		err = k_sem_take(&sem_per_sync_lost, K_FOREVER);
		if (err) {
			printk("\nfailed (err %d)\n", err);
			return;
		}
		printk("\nPeriodic sync lost.\n");


	} while (true);
}
