#include "sx1276_lora.h"

/* SX127x driver
 * Copyright (c) 2013 Semtech
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

SX1276_lora::SX1276_lora(SX1276& r) : m_xcvr(r)
{
    RegModemConfig.octet = m_xcvr.read_reg(REG_LR_MODEMCONFIG);
    RegModemConfig2.octet = m_xcvr.read_reg(REG_LR_MODEMCONFIG2);
    RegTest31.octet = m_xcvr.read_reg(REG_LR_TEST31);
    
    // CRC for TX is disabled by default
    setRxPayloadCrcOn(true);
}

SX1276_lora::~SX1276_lora()
{
}
    
void SX1276_lora::write_fifo(uint8_t len)
{
    int i;
    
    m_xcvr.m_cs = 0;
    m_xcvr.m_spi.write(REG_FIFO | 0x80); // bit7 is high for writing to radio
    
    for (i = 0; i < len; i++) {
        m_xcvr.m_spi.write(m_xcvr.tx_buf[i]);
    }
    m_xcvr.m_cs = 1;
}

void SX1276_lora::read_fifo(uint8_t len)
{
    int i;
     
    m_xcvr.m_cs = 0;
    m_xcvr.m_spi.write(REG_FIFO); // bit7 is low for reading from radio
    for (i = 0; i < len; i++) {
        m_xcvr.rx_buf[i] = m_xcvr.m_spi.write(0);
    }
    m_xcvr.m_cs = 1;
}

void SX1276_lora::enable()
{
    m_xcvr.set_opmode(RF_OPMODE_SLEEP);
    
    m_xcvr.RegOpMode.bits.LongRangeMode = 1;
    m_xcvr.write_reg(REG_OPMODE, m_xcvr.RegOpMode.octet);
    m_xcvr.RegDioMapping1.bits.Dio0Mapping = 0;    // DIO0 to RxDone
    m_xcvr.RegDioMapping1.bits.Dio1Mapping = 0;
    m_xcvr.write_reg(REG_DIOMAPPING1, m_xcvr.RegDioMapping1.octet);
    
    // todo: read LoRa regsiters
    m_xcvr.set_opmode(RF_OPMODE_STANDBY);            
}

uint8_t SX1276_lora::getCodingRate(bool from_rx)
{
    if (from_rx)    // expected RegModemStatus was read on RxDone interrupt
        return RegModemStatus.bits.RxCodingRate;    
    else     // transmitted coding rate...
            return RegModemConfig.sx1276bits.CodingRate;
}



void SX1276_lora::setCodingRate(uint8_t cr)
{
    if (!m_xcvr.RegOpMode.bits.LongRangeMode)
        return;
    RegModemConfig.sx1276bits.CodingRate = cr;
    m_xcvr.write_reg(REG_LR_MODEMCONFIG, RegModemConfig.octet);
}



bool SX1276_lora::getHeaderMode(void)
{
        return RegModemConfig.sx1276bits.ImplicitHeaderModeOn;
}

void SX1276_lora::setHeaderMode(bool hm)
{
    RegModemConfig.sx1276bits.ImplicitHeaderModeOn = hm;
    m_xcvr.write_reg(REG_LR_MODEMCONFIG, RegModemConfig.octet);
}


uint8_t SX1276_lora::getBw(void)
{
    return RegModemConfig.sx1276bits.Bw;
}

void SX1276_lora::setBw(uint8_t bw)
{
    if (!m_xcvr.RegOpMode.bits.LongRangeMode)
        return;
    RegModemConfig.sx1276bits.Bw = bw;
    m_xcvr.write_reg(REG_LR_MODEMCONFIG, RegModemConfig.octet);
}



uint8_t SX1276_lora::getSf(void)
{
    return RegModemConfig2.sx1276bits.SpreadingFactor;
}

void SX1276_lora::set_nb_trig_peaks(int n)
{
    RegTest31.bits.detect_trig_same_peaks_nb = n;
    m_xcvr.write_reg(REG_LR_TEST31, RegTest31.octet);
}


bool SX1276_lora::getRxPayloadCrcOn(void)
{
        return RegModemConfig2.sx1276bits.RxPayloadCrcOn;
}


void SX1276_lora::setRxPayloadCrcOn(bool on)
{
        RegModemConfig2.sx1276bits.RxPayloadCrcOn = on;
        m_xcvr.write_reg(REG_LR_MODEMCONFIG2, RegModemConfig2.octet); 
}



bool SX1276_lora::getAgcAutoOn(void)
{
        RegModemConfig3.octet = m_xcvr.read_reg(REG_LR_MODEMCONFIG3);
        return RegModemConfig3.sx1276bits.AgcAutoOn;
}

void SX1276_lora::setAgcAutoOn(bool on)
{
        RegModemConfig3.sx1276bits.AgcAutoOn = on;
        m_xcvr.write_reg(REG_LR_MODEMCONFIG3, RegModemConfig3.octet);
}



void SX1276_lora::start_tx(uint8_t len)
{
        // PA_BOOST on LF, RFO on HF
       if (!m_xcvr.RegPaConfig.bits.PaSelect) 
        {
             m_xcvr.RegPaConfig.bits.PaSelect = 1;
             m_xcvr.write_reg(REG_PACONFIG, m_xcvr.RegPaConfig.octet);
        }        
                  
                    
    // DIO0 to TxDone
    if (m_xcvr.RegDioMapping1.bits.Dio0Mapping != 1) {
        m_xcvr.RegDioMapping1.bits.Dio0Mapping = 1;
        m_xcvr.write_reg(REG_DIOMAPPING1, m_xcvr.RegDioMapping1.octet);
    }
    
    // set FifoPtrAddr to FifoTxPtrBase
    m_xcvr.write_reg(REG_LR_FIFOADDRPTR, m_xcvr.read_reg(REG_LR_FIFOTXBASEADDR));
    
    // write PayloadLength bytes to fifo
    write_fifo(len);


        
    // radio doesnt provide FhssChangeChannel with channel=0 for TX    
    if (RegHopPeriod > 0)
        m_xcvr.write_u24(REG_FRFMSB, m_xcvr.frfs[0]);
        
    m_xcvr.set_opmode(RF_OPMODE_TRANSMITTER);
}

void SX1276_lora::start_rx()
{
    if (!m_xcvr.RegOpMode.bits.LongRangeMode)
        return;


        
    if (m_xcvr.RegDioMapping1.bits.Dio0Mapping != 0) {
        m_xcvr.RegDioMapping1.bits.Dio0Mapping = 0;    // DIO0 to RxDone
        m_xcvr.write_reg(REG_DIOMAPPING1, m_xcvr.RegDioMapping1.octet);
    }
    
    m_xcvr.write_reg(REG_LR_FIFOADDRPTR, m_xcvr.read_reg(REG_LR_FIFORXBASEADDR));
    
    // shouldn't be necessary, radio should provide FhssChangeChannel with channel=0 for RX  
    if (RegHopPeriod > 0)
        m_xcvr.write_u24(REG_FRFMSB, m_xcvr.frfs[0]);
    
    m_xcvr.set_opmode(RF_OPMODE_RECEIVER);
}

float SX1276_lora::get_pkt_rssi()
{
    /* TODO: calculating with pktSNR to give meaningful result below noise floor */
        return RegPktRssiValue - 137;
}

service_action_e SX1276_lora::service()
{
    if (m_xcvr.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER) {
        if (poll_vh) {
            RegIrqFlags.octet = m_xcvr.read_reg(REG_LR_IRQFLAGS);
            if (RegIrqFlags.bits.ValidHeader) {
                RegIrqFlags.octet = 0;
                RegIrqFlags.bits.ValidHeader = 1;
                m_xcvr.write_reg(REG_LR_IRQFLAGS, RegIrqFlags.octet);
                printf("VH\r\n");
            }
        }
    }
    
    // FhssChangeChannel
    if (m_xcvr.RegDioMapping1.bits.Dio1Mapping == 1) {
        if (m_xcvr.dio1) {
            RegHopChannel.octet = m_xcvr.read_reg(REG_LR_HOPCHANNEL);    
            m_xcvr.write_u24(REG_FRFMSB, m_xcvr.frfs[RegHopChannel.bits.FhssPresentChannel]);
            printf("hopch:%d\r\n", RegHopChannel.bits.FhssPresentChannel);
            RegIrqFlags.octet = 0;
            RegIrqFlags.bits.FhssChangeChannel = 1;
           m_xcvr. write_reg(REG_LR_IRQFLAGS, RegIrqFlags.octet);
            
        }
    }
    
    if (m_xcvr.dio0 == 0)
        return SERVICE_NONE;
        
    switch (m_xcvr.RegDioMapping1.bits.Dio0Mapping) {
        case 0: // RxDone
            /* user checks for CRC error in IrqFlags */
            RegIrqFlags.octet = m_xcvr.read_reg(REG_LR_IRQFLAGS);  // save flags
            RegHopChannel.octet = m_xcvr.read_reg(REG_LR_HOPCHANNEL);
            if (RegIrqFlags.bits.FhssChangeChannel) {
                m_xcvr.write_u24(REG_FRFMSB, m_xcvr.frfs[RegHopChannel.bits.FhssPresentChannel]);
            }
            //printf("[%02x]", RegIrqFlags.octet);
            m_xcvr.write_reg(REG_LR_IRQFLAGS, RegIrqFlags.octet); // clear flags in radio
            
            /* any register of interest on received packet is read(saved) here */        
            RegModemStatus.octet = m_xcvr.read_reg(REG_LR_MODEMSTAT);          
            RegPktSnrValue = m_xcvr.read_reg(REG_LR_PKTSNRVALUE);
            RegPktRssiValue = m_xcvr.read_reg(REG_LR_PKTRSSIVALUE);
            RegRxNbBytes = m_xcvr.read_reg(REG_LR_RXNBBYTES);
    
            m_xcvr.write_reg(REG_LR_FIFOADDRPTR, m_xcvr.read_reg(REG_LR_FIFORXCURRENTADDR));
            read_fifo(RegRxNbBytes);
            return SERVICE_READ_FIFO;
        case 1: // TxDone
            RegIrqFlags.octet = 0;
            RegIrqFlags.bits.TxDone = 1;
            m_xcvr.write_reg(REG_LR_IRQFLAGS, RegIrqFlags.octet);                  
            return SERVICE_TX_DONE;        
    } // ...switch (RegDioMapping1.bits.Dio0Mapping)
    
    return SERVICE_ERROR;    
}