#include "basic.h"
#include "nrf.h"

// 用于线程调度
void yield(void) { schedule(); }
// 创建一个uBit
MicroBit uBit;
// 获取microbit IO句柄
MicroBitPin *getMicroBitPin(int id) {
    switch (id) {
        case MICROBIT_ID_IO_P0: return &uBit.io.P0;
        case MICROBIT_ID_IO_P1: return &uBit.io.P1;
        case MICROBIT_ID_IO_P2: return &uBit.io.P2;
        case MICROBIT_ID_IO_P3: return &uBit.io.P3;
        case MICROBIT_ID_IO_P4: return &uBit.io.P4;
        case MICROBIT_ID_IO_P5: return &uBit.io.P5;
        case MICROBIT_ID_IO_P6: return &uBit.io.P6;
        case MICROBIT_ID_IO_P7: return &uBit.io.P7;
        case MICROBIT_ID_IO_P8: return &uBit.io.P8;
        case MICROBIT_ID_IO_P9: return &uBit.io.P9;
        case MICROBIT_ID_IO_P10: return &uBit.io.P10;
        case MICROBIT_ID_IO_P11: return &uBit.io.P11;
        case MICROBIT_ID_IO_P12: return &uBit.io.P12;
        case MICROBIT_ID_IO_P13: return &uBit.io.P13;
        case MICROBIT_ID_IO_P14: return &uBit.io.P14;
        case MICROBIT_ID_IO_P15: return &uBit.io.P15;
        case MICROBIT_ID_IO_P16: return &uBit.io.P16;
        case MICROBIT_ID_IO_P19: return &uBit.io.P19;
        case MICROBIT_ID_IO_P20: return &uBit.io.P20;
        default: return NULL;
    }
}
// dal系统初始化
void init(){
    uBit.init();
#if CONFIG_ENABLED(MICROBIT_RADIO_BRUN)
    radioBrunInit();
#endif
}


// 无线烧录相关
#if CONFIG_ENABLED(MICROBIT_RADIO_BRUN)
radio_callback_t m_data_pkt_cb;
RadioFrameBuffer             *rxBuf;     //无线缓冲区指针
RadioFrameBuffer              mybuf;     //无线缓冲区
const char *version = "V0.1";
const int8_t MICROBIT_BLE_POWER_LEVEL[] = {-30, -20, -16, -12, -8, -4, 0, 4};
char cmd[6];


void radioBrunInit(){
    radioBrunInit();
    RadioConfig userRadioConfig;
    //初始化无线参数
    userRadioConfig.baseAddress = getAddress();
    userRadioConfig.channel = getChannel();
    userRadioConfig.frequencyBand = MICROBIT_RADIO_DEFAULT_FREQUENCY;
    userRadioConfig.power = MICROBIT_RADIO_DEFAULT_TX_POWER;
    userRadioConfig.speed = RADIO_MODE_MODE_Nrf_2Mbit;
    userRadioConfig.maxSize = MICROBIT_RADIO_MAX_PACKET_SIZE;
    //设置无线消息解析回调函数
    radioRegisterCallback(radioTransmissionHandle);
    //打开无线传输
    radioEnable(&userRadioConfig);
}

void radioBrunHandle(void)
{
    while(1){
        //如果断开连接，则开始广播
        if(!isConnection())
        {
            radioBroadcast();
        }
        fiber_sleep(500);
    }
}

int radioTransmissionHandle(uint8_t *p_data)
{
    //uBit.serial.printf((const char *)p_data);
    //获取命令
    memcpy(cmd, p_data, 5);
    cmd[5]='\0';

    if(!strcmp(cmd,  "|1|0|"))//请求连接
    {
        char data[50];
        char microbit_name[20];
        getBoardName(microbit_name);//获取已经设置好的名称
        sprintf(data,"|1|0|0x%x|%d|%s|",getAddress(),getChannel(),microbit_name);
        radioSend((uint8_t *)data,strlen(data));//返回通道地址以及名称
        saveConnectFlag(true);//更新connected标志
    }else if(!strcmp(cmd, "|1|1|"))//请求擦除flash
    {
        radioSend((uint8_t *)"|1|1|-11|please jump to boot|",sizeof("|1|1|-11|please jump to boot|"));
    }else if(!strcmp(cmd,  "|1|3|"))//请求取消连接
    {
        radioDisconnect(cmd, p_data);
    }else if(!strcmp(cmd,  "|2|1|"))//请求设置microbir名称
    {
        char data[50];
        char microbit_name[20];
        uint8_t *p = p_data + 5, i=0;
        //获取名称
        while(*p != '|'){
            data[i++] = *(p++);
        }
        data[i] = '\0';
        memset(microbit_name, 0, 20);
        memcpy(microbit_name, data, strlen(data));
        microbit_name[strlen(data)] = '\0';
        //设置名称
        setBoardName(microbit_name);
        radioSend((uint8_t *)"|2|1|ok|",sizeof("|2|1|ok|"));
    }else if(!strcmp(cmd,  "|2|2|"))//请求获取microbit名称
    {
        char data[50];
        char microbit_name[20];
        //获取名称
        getBoardName(microbit_name);
        sprintf(data,"|2|2|%s|",microbit_name);
        radioSend((uint8_t *)data,strlen(data));
    }else if(!strcmp(cmd,  "|2|3|"))//请求获取boot版本
    {
        char data[50];
        sprintf(data,"|2|3|%s|",version);
        radioSend((uint8_t *)data,strlen(data));
    }else if(!strcmp(cmd,  "|3|0|"))//请求跳转到app运行
    {
        radioSend((uint8_t *)"|3|0|ok|",sizeof("|3|0|ok|"));
    }else if(!strcmp(cmd,  "|3|1|"))//请求跳转到boot升级app
    {
        uint8_t buf[1024];
        uint32_t addr = VectorTableBase, data;
        //关掉所有中断、擦除app中断向量表
        __disable_irq();
        nvmcPageErase(0);//1024
        //获取boot中断向量表
        for(int i = 0; i < 1024; )
        {
            data = *(__IO uint32_t*)(addr);
            buf[i] = data & 0xff;
            buf[i+1] = (data>>8) & 0xff;
            buf[i+2] = (data>>16) & 0xff;
            buf[i+3] = (data>>24) & 0xff;
            i += 4;
            addr += 4;
        }
        //更新中断向量表
        nvmcWriteBytes(0,buf,1024);
        radioSend((uint8_t *)"|3|1|ok|",sizeof("|3|1|ok|"));
        //系统复位
        NVIC_SystemReset();

    }else if(!strcmp(cmd,  "|3|2|"))//获取microbit运行状态
    {
        radioSend((uint8_t *)"|3|2|app|",sizeof("|3|2|app|"));
    }
}

uint32_t getAddress()
{
    return *(__IO uint32_t*)(ApplicationCheckAddress + 32);
}

uint8_t getChannel()
{
    return (uint8_t)((*(__IO uint32_t*)(ApplicationCheckAddress + 36)) & 0x000000ff);
}

void setBoardName(char *name)
{
    int i;
    uint8_t buf[1024];
    uint32_t addr = ApplicationCheckAddress, data;

    for(i = 0; i < 1024; )
    {
        data = *(__IO uint32_t*)(addr);
        buf[i] = data & 0xff;
        buf[i+1] = (data>>8) & 0xff;
        buf[i+2] = (data>>16) & 0xff;
        buf[i+3] = (data>>24) & 0xff;
        i += 4;
        addr += 4;
    }

    nvmcPageErase(ApplicationCheckAddress);//1024

    for(i=0; i<20; i++)
    {
        buf[8+i] = name[i];
    }

    nvmcWriteBytes(ApplicationCheckAddress, buf, 1024);
}

int radioDisconnect(char *cmd, uint8_t *p_data)
{
    uint8_t *p = p_data + 5,i=0;
    char address[20], channel[20], name[20], microbit_name[20];
    char addrbuf[20];
    uint8_t _channel;
    
    if(strcmp(cmd, "|1|3|")) return 1;
    
    while(*p != '|'){
        address[i++] = *(p++);
    }
    address[i] = '\0';
    
    i=0;
    p++;
    while(*p != '|'){
        channel[i++] = *(p++);
    }
    channel[i] = '\0';
    
    i=0;
    p++;
    while(*p != '|'){
        name[i++] = *(p++);
    }
    name[i] = '\0';

    //_address = atoi(address);
    sprintf(addrbuf, "0x%x",getAddress());
    _channel = atoi(channel);
    
    getBoardName(microbit_name);
    if(!strcmp(addrbuf, address) &&
         _channel == getChannel() &&
       !strcmp(name, microbit_name))
    {
        saveConnectFlag(false);
        radioSend((uint8_t *)"|1|3|ok|",sizeof("|1|3|ok|"));
        return 0;
    }
    radioSend((uint8_t *)"|1|3|-8|name mismatch|",sizeof("|1|3|-8|name mismatch|"));
    return 0;
}

void saveConnectFlag(bool flag)
{
    int i;
    uint8_t buf[1024];
    uint32_t addr = ApplicationCheckAddress, data;
    
    for(i = 0; i < 1024; )
    {
        data = *(__IO uint32_t*)(addr);
        buf[i] = data & 0xff;
        buf[i+1] = (data>>8) & 0xff;
        buf[i+2] = (data>>16) & 0xff;
        buf[i+3] = (data>>24) & 0xff;
        i += 4;
        addr += 4;
    }
    
    nvmcPageErase(ApplicationCheckAddress);//1024
    buf[28] = (uint8_t)flag;
    buf[29] = 0;
    buf[30] = 0;
    buf[31] = 0;
    nvmcWriteBytes(ApplicationCheckAddress, buf, 1024);
}

void getBoardName(char *name)
{
    int i;
    uint32_t data, addr = ApplicationCheckAddress + 8;
    memset(name, 0, 20);
    for(i=0; i<20; )
    {
        data = *(__IO uint32_t*)(addr);
        name[i] = data & 0xff;
        name[i+1] = (data>>8) & 0xff;
        name[i+2] = (data>>16) & 0xff;
        name[i+3] = (data>>24) & 0xff;
        addr += 4;
        i += 4;
    }
    if((uint8_t)name[0] == 0xFF &&
       (uint8_t)name[1] == 0xFF &&
       (uint8_t)name[2] == 0xFF &&
       (uint8_t)name[3] == 0xFF){
        memcpy(name, "default", strlen("default"));
        name[strlen("default")] = '\0';
    }
}

void radioBroadcast(void)
{
    char data[50], microbit_name[20];
    getBoardName(microbit_name);
    sprintf(data,"|2|0|0x%x|%d|%s|",getAddress(),getChannel(),microbit_name);
    radioSend((uint8_t *)data,strlen(data));
}

bool isConnection(void)
{
    uint8_t flag = *(__IO uint32_t*)(ApplicationCheckAddress + 28) & 0xff;
    if(flag == 0xFF || flag == 0)
        return false;
    else if(flag == 1)
        return true;
    return false;
}

int radioEnable(RadioConfig *config)
{
    uint8_t radio_group = config->channel;
    uint32_t radio_address = config->baseAddress;
    int radio_power = config->power;
    int radio_frequencyBand = config->frequencyBand;
    int radio_speed = config->speed;
    int radio_maxSize = config->maxSize;

    // If this is the first time we've been enable, allocate out receive buffers.
    rxBuf = &mybuf;//(RadioFrameBuffer *)malloc(sizeof(RadioFrameBuffer) * 1);

    // Enable the High Frequency clock on the processor. This is a pre-requisite for
    // the RADIO module. Without this clock, no communication is possible.
    NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    NRF_CLOCK->TASKS_HFCLKSTART = 1;
    while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);

    // Bring up the nrf51822 RADIO module in Nordic's proprietary 1MBps packet radio mode.
    radioSetTransmitPower(radio_power);
    radioSetFrequencyBand(radio_frequencyBand);

    // Configure for 1Mbps throughput.
    // This may sound excessive, but running a high data rates reduces the chances of collisions...
    NRF_RADIO->MODE = radio_speed;

    // Configure the addresses we use for this protocol. We run ANONYMOUSLY at the core.
    // A 40 bit addresses is used. The first 32 bits match the ASCII character code for "uBit".
    // Statistically, this provides assurance to avoid other similar 2.4GHz protocols that may be in the vicinity.
    // We also map the assigned 8-bit GROUP id into the PREFIX field. This allows the RADIO hardware to perform
    // address matching for us, and only generate an interrupt when a packet matching our group is received.
    NRF_RADIO->BASE0 = radio_address;

    // Join the default group. This will configure the remaining byte in the RADIO hardware module.
    radioSetGroup(radio_group);

    // The RADIO hardware module supports the use of multiple addresses, but as we're running anonymously, we only need one.
    // Configure the RADIO module to use the default address (address 0) for both send and receive operations.
    NRF_RADIO->TXADDRESS = 0;
    NRF_RADIO->RXADDRESSES = 1;

    // Packet layout configuration. The nrf51822 has a highly capable and flexible RADIO module that, in addition to transmission
    // and reception of data, also contains a LENGTH field, two optional additional 1 byte fields (S0 and S1) and a CRC calculation.
    // Configure the packet format for a simple 8 bit length field and no additional fields.
    NRF_RADIO->PCNF0 = (PACKET_S1_FIELD_SIZE     << RADIO_PCNF0_S1LEN_Pos) |
                       (PACKET_S0_FIELD_SIZE     << RADIO_PCNF0_S0LEN_Pos) |
                       (PACKET_LENGTH_FIELD_SIZE << RADIO_PCNF0_LFLEN_Pos); //lint !e845 "The right argument to operator '|' is certain to be 0"

    // Packet configuration
    NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_DISABLE << RADIO_PCNF1_WHITEEN_Pos) |
                       (RADIO_PCNF1_ENDIAN_BIG      << RADIO_PCNF1_ENDIAN_Pos)  |
                       (PACKET_BASE_ADDRESS_LENGTH  << RADIO_PCNF1_BALEN_Pos)   |
                       (PACKET_STATIC_LENGTH        << RADIO_PCNF1_STATLEN_Pos) |
                       (radio_maxSize               << RADIO_PCNF1_MAXLEN_Pos);

    // Most communication channels contain some form of checksum - a mathematical calculation taken based on all the data
    // in a packet, that is also sent as part of the packet. When received, this calculation can be repeated, and the results
    // from the sender and receiver compared. If they are different, then some corruption of the data ahas happened in transit,
    // and we know we can't trust it. The nrf51822 RADIO uses a CRC for this - a very effective checksum calculation.
    // Enable automatic 16bit CRC generation and checking, and configure how the CRC is calculated.
    NRF_RADIO->CRCCNF = RADIO_CRCCNF_LEN_Two;
    NRF_RADIO->CRCINIT = 0xFFFF;
    NRF_RADIO->CRCPOLY = 0x11021;

    // Set the start random value of the data whitening algorithm. This can be any non zero number.
    NRF_RADIO->DATAWHITEIV = 0x18;

    // Set up the RADIO module to read and write from our internal buffer.
    NRF_RADIO->PACKETPTR = (uint32_t)rxBuf;

    // Configure the hardware to issue an interrupt whenever a task is complete (e.g. send/receive).
    NRF_RADIO->INTENSET = 0x00000008;
    NVIC_ClearPendingIRQ(RADIO_IRQn);
    NVIC_EnableIRQ(RADIO_IRQn);

    NRF_RADIO->SHORTS |= RADIO_SHORTS_ADDRESS_RSSISTART_Msk;

    // Start listening for the next packet
    NRF_RADIO->EVENTS_READY = 0;
    NRF_RADIO->TASKS_RXEN = 1;
    while(NRF_RADIO->EVENTS_READY == 0);

    NRF_RADIO->EVENTS_END = 0;
    NRF_RADIO->TASKS_START = 1;

    return 0;
}

int radioSendConfig(RadioFrameBuffer *buffer)
{
    if (buffer == NULL)
        return -1;

    // Firstly, disable the Radio interrupt. We want to wait until the trasmission completes.
    NVIC_DisableIRQ(RADIO_IRQn);

    // Turn off the transceiver.
    NRF_RADIO->EVENTS_DISABLED = 0;
    NRF_RADIO->TASKS_DISABLE = 1;
    while(NRF_RADIO->EVENTS_DISABLED == 0);

    // Configure the radio to send the buffer provided.
    NRF_RADIO->PACKETPTR = (uint32_t) buffer;

    // Turn on the transmitter, and wait for it to signal that it's ready to use.
    NRF_RADIO->EVENTS_READY = 0;
    NRF_RADIO->TASKS_TXEN = 1;
    while (NRF_RADIO->EVENTS_READY == 0);

    // Start transmission and wait for end of packet.
    NRF_RADIO->TASKS_START = 1;
    NRF_RADIO->EVENTS_END = 0;
    while(NRF_RADIO->EVENTS_END == 0);

    // Return the radio to using the default receive buffer
    NRF_RADIO->PACKETPTR = (uint32_t) rxBuf;

    // Turn off the transmitter.
    NRF_RADIO->EVENTS_DISABLED = 0;
    NRF_RADIO->TASKS_DISABLE = 1;
    while(NRF_RADIO->EVENTS_DISABLED == 0);

    // Start listening for the next packet
    NRF_RADIO->EVENTS_READY = 0;
    NRF_RADIO->TASKS_RXEN = 1;
    while(NRF_RADIO->EVENTS_READY == 0);

    NRF_RADIO->EVENTS_END = 0;
    NRF_RADIO->TASKS_START = 1;

    // Re-enable the Radio interrupt.
    NVIC_ClearPendingIRQ(RADIO_IRQn);
    NVIC_EnableIRQ(RADIO_IRQn);

    return 0;
}

int radioSend(uint8_t *buffer, uint8_t len)
{
    RadioFrameBuffer buf;

    buf.length = len + MICROBIT_RADIO_HEADER_SIZE - 1;
    buf.version = 1;
    buf.group = 0;
    buf.protocol = MICROBIT_RADIO_PROTOCOL_DATAGRAM;
    memcpy(buf.payload, buffer, len);
    return radioSendConfig(&buf);
}

int radioDisable()
{
    return 0;
}

int radioSetGroup(uint8_t _group)
{
    NRF_RADIO->PREFIX0 = (uint32_t)_group;
    return 0;
}

void radioRegisterCallback(radio_callback_t callback_handler)
{
    m_data_pkt_cb = callback_handler;
}

int radioSetTransmitPower(int power)
{
    NRF_RADIO->TXPOWER = (uint32_t)MICROBIT_BLE_POWER_LEVEL[power];
    return 0;
}

int radioSetFrequencyBand(int band)
{
    NRF_RADIO->FREQUENCY = (uint32_t)band;
    return 0;
}

void nvmcPageErase(uint32_t address)
{
    // Enable erase.
    NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een;
    while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}

    // Erase the page
    NRF_NVMC->ERASEPAGE = address;
    while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
    
    NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
    while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
}

void nvmcWriteBytes(uint32_t address, const uint8_t * src, uint32_t num_bytes)
{
  uint32_t i;
    NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
    while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {}

  for(i=0;i<num_bytes;)
  {
        *(__IO uint32_t*)address = (uint32_t)src[i] | ((uint32_t)(src[i+1]))<<8 | 
                                   ((uint32_t)(src[i+2]))<<16 | ((uint32_t)(src[i+3]))<<24;
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
        i+=4;
        address+=4;
  }

  NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
}

////////////////////////////////////////////IRQHandler///////////////////////////////////////////////
void RADIO_IRQHandler(void)
{
    if(NRF_RADIO->EVENTS_READY)
    {
        NRF_RADIO->EVENTS_READY = 0;

        // Start listening and wait for the END event
        NRF_RADIO->TASKS_START = 1;
    }
    if(NRF_RADIO->EVENTS_END)
    {
        NRF_RADIO->EVENTS_END = 0;
        if(NRF_RADIO->CRCSTATUS == 1)
        {
            //int sample = (int)NRF_RADIO->RSSISAMPLE;
            if(m_data_pkt_cb) m_data_pkt_cb((uint8_t *)rxBuf+4);
        }
        // Start listening and wait for the END event
        NRF_RADIO->TASKS_START = 1;
    }
    memset(rxBuf,0,50);
}

#endif