#include "lora.h"
#include <stddef.h>
#include "sx126x_hal_stm32.h"

static sx126x_stm32_context_t radio =
{
    .spi        = NULL, // set in main.c

    .nss_port   = LORA_NSS_GPIO_Port,
    .nss_pin    = LORA_NSS_Pin,

    .reset_port = LORA_RST_GPIO_Port,
    .reset_pin  = LORA_RST_Pin,

    .busy_port  = LORA_BUSY_GPIO_Port,
    .busy_pin   = LORA_BUSY_Pin
};

static volatile bool lora_dio1_pending = false;   // interrupt flag
static volatile bool lora_tx_in_progress = false; // used in while loop to clear after tx

sx126x_status_t LoRa_Init(SPI_HandleTypeDef *spi)
{
    sx126x_status_t status;

    if (spi == NULL){return SX126X_STATUS_ERROR;}

    radio.spi = spi;

    lora_dio1_pending = false;
    lora_tx_in_progress = false;

    status = sx126x_reset(&radio);
    if (status != SX126X_STATUS_OK){return status;}

    // standby with its internal RC oscillator.
    // generic driver passes the radio pointer down to my HAL code in sx126x_hal_stm32.c
    // probably using sx126x_hal_write
    status = sx126x_set_standby(&radio,SX126X_STANDBY_CFG_RC);
    if (status != SX126X_STATUS_OK){return status;}

    // i guess the SX1262 supports two internal voltage regulator modes, DCDC is the better one
    status = sx126x_set_reg_mode(&radio,SX126X_REG_MODE_DCDC);
    if (status != SX126X_STATUS_OK){return status;}

    // Choose DIO3 to power the module's TCXO at 3.0 V with a 5 ms startup delay
    // becuase why not
    status = sx126x_set_dio3_as_tcxo_ctrl(&radio,SX126X_TCXO_CTRL_3_0V,
      sx126x_convert_timeout_in_ms_to_rtc_step(5));
    if (status != SX126X_STATUS_OK){return status;}

    // SX1262 code drives DIO2 to control the module's TX/RX RF switch.
    // do this once, then use sx126x_set_tx, sx126x_set_rx to switch
    status = sx126x_set_dio2_as_rf_sw_ctrl(&radio,true);
    if (status != SX126X_STATUS_OK){return status;}

    // calibration stuff on the SX1262.
    status = sx126x_cal(&radio,SX126X_CAL_ALL);
    if (status != SX126X_STATUS_OK){return status;}

    // reject circuitry for the 902-928 MHz band.
    status = sx126x_cal_img_in_mhz(&radio,902,928);
    if (status != SX126X_STATUS_OK){return status;}

    // LoRa as modulation type
    status = sx126x_set_pkt_type(&radio,SX126X_PKT_TYPE_LORA);
    if (status != SX126X_STATUS_OK){return status;}

    // use sync word 0x12 so TX and RX recognize the same class of LoRa packets.
      // sx126x_set_lora_sync_word(&tx_radio, 0x12);
      // sx126x_set_lora_sync_word(&rx_radio, 0x12);
    status = sx126x_set_lora_sync_word(&radio,0x12);
    if (status != SX126X_STATUS_OK){return status;}

    // RF carrier frequency to 915 MHz. USA baby!
    status = sx126x_set_rf_freq(&radio,915000000UL);
    if (status != SX126X_STATUS_OK) { return status; }

    // rf stuff that is important
      // arduino radio lib, you also setup this stuff
      // radio.begin(
      //     915.0,  // frequency
      //     125.0,  // bandwidth
      //     7,      // spreading factor
      //     5,      // coding rate denominator: 4/5
      //     0x12,   // sync word
      //     14,     // Transmit output power in dBm.
      //     8,      // LoRa preamble length in symbols.
      //     3.0,    // TCXO supply voltage driven from DIO3, in volts.
      //     false   // false selects DC-DC mode; true selects LDO mode.
      // );

        // chnage setting here for distance tetsting

    sx126x_mod_params_lora_t modulation =
    {
        // spreading factor 7 for relatively fast data rate and shorter airtime.
        .sf   = SX126X_LORA_SF7,
        // 125 kHz LoRa channel bandwidth.
        .bw   = SX126X_LORA_BW_125,
        // coding rate 4/5, adding one error-correction bit for every four data bits.
        .cr   = SX126X_LORA_CR_4_5,
        // Disable low-data-rate optimization because SF7 at 125 kHz does not need it.
        .ldro = 0
    };

    // apply radio params
    status = sx126x_set_lora_mod_params(&radio,&modulation);
    if (status != SX126X_STATUS_OK) { return status; }

    // SX1262 power amplifier setting, not sure what these do
    sx126x_pa_cfg_params_t pa_config =
    {
        .pa_duty_cycle = 0x04,
        .hp_max        = 0x07,
        .device_sel    = 0x00,
        .pa_lut        = 0x01
    };
    // apply
    status = sx126x_set_pa_cfg(&radio,&pa_config);
    if (status != SX126X_STATUS_OK) { return status; }

    // set transmit power to +20 dBm watts
    // +22 dBm watts is max i believe 
    // SX126X_RAMP_200_US -> 200 microseconds to ramp the PA up to that power.

        // change power here for in city distance testing

    status = sx126x_set_tx_params(&radio,20,SX126X_RAMP_200_US);
    if (status != SX126X_STATUS_OK) { return status; }

    // Set both the TX and RX buffer starting addresses to offset 0x00.
    status = sx126x_set_buffer_base_address(&radio,0x00,0x00);

    return status;
}

sx126x_status_t LoRa_Send(
    const uint8_t *payload,
    uint8_t payload_length)
{
    sx126x_status_t status;

    if ((radio.spi == NULL) ||
        (payload == NULL) ||
        (payload_length == 0U) ||
        lora_tx_in_progress)
    {
        return SX126X_STATUS_ERROR;
    }

    // put radio on standby with its internal SX126X_STANDBY_CFG_RC
    status = sx126x_set_standby(&radio,SX126X_STANDBY_CFG_RC);

    if (status != SX126X_STATUS_OK){return status;}

    // this is found in sx126x.h
    // packet_params inherits struct definition, its a type sx126x_pkt_params_lora_t
    sx126x_pkt_params_lora_t packet_params =
    {
        .preamble_len_in_symb = 8,  // LoRa preamble length, measured in symbols.
        // symbol is some rf blackmagic chirp thing

        .header_type = SX126X_LORA_PKT_EXPLICIT,
        // include a packet header; found in sx126x_lora_pkt_len_modes_t

        .pld_len_in_bytes = payload_length,
        // payload bytes to transmit, function arg from above

        .crc_is_on = true,
        // Enable the LoRa payload CRC. its a error-detection value added to the packet
        // Cyclic Redundancy Check

        .invert_iq_is_on = false
        // Use normal, non-inverted LoRa I/Q polarity.
        // i think the the receiver must use the same setting.
    };

    status = sx126x_set_lora_pkt_params(&radio, &packet_params);

    if (status != SX126X_STATUS_OK){return status;}

    // clears stored interrupt event flags inside the SX1262:
    status = sx126x_clear_irq_status(&radio, SX126X_IRQ_ALL);

    if (status != SX126X_STATUS_OK){return status;}

    status = sx126x_set_dio_irq_params(
        &radio,
        SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT,
        SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT,
        SX126X_IRQ_NONE,
        SX126X_IRQ_NONE);

    if (status != SX126X_STATUS_OK){return status;}

    // copy payload bytes to SX1262’s buffer before sx126x_set_tx() starts transmission
    status = sx126x_write_buffer(
        &radio,
        0x00,
        payload,
        payload_length);

    if (status != SX126X_STATUS_OK)
    {
        return status;
    }

    lora_dio1_pending = false;
    lora_tx_in_progress = true;

    // send that jawn, but only wait 3000 ms for dat jawn
    status = sx126x_set_tx(&radio, 3000);

    if (status != SX126X_STATUS_OK){lora_tx_in_progress = false;}

    return status;
}

void LoRa_OnDio1Interrupt(void)
{
    // called in main.c
    lora_dio1_pending = true;
}

lora_event_t LoRa_Process(void)
{
    // irq_status set to "no IRQ flags set.""
    sx126x_irq_mask_t irq_status = SX126X_IRQ_NONE;

    // my local interrupt flag, retrun if not set lora_dio1_pending
    if (!lora_dio1_pending){return LORA_EVENT_NONE;}

    // spi call
    // store the interrupt flags in my irq_status
    // irq_status can store two flags as a bitmask 
    // irq_status = SX126X_IRQ_RX_DONE |SX126X_IRQ_CRC_ERROR; its done but there is a packet error
    if (sx126x_get_irq_status(&radio, &irq_status) !=SX126X_STATUS_OK)
    {
        lora_tx_in_progress = false;
        return LORA_EVENT_IRQ_ERROR;
    }

    // spi call
    // clear SX126X internal IRQ, does not change irq_status, irq_status jsut tells what to erase
    if (sx126x_clear_irq_status(&radio, irq_status) != SX126X_STATUS_OK)
    {
        lora_tx_in_progress = false;
        return LORA_EVENT_IRQ_ERROR;
    }

    // oh no, we have to do stuff, the interrupt has been triggered
    // set back to false
    lora_dio1_pending = false;

    /*
    i think this is the best order
    TX_DONE
    CRC_ERROR / HEADER_ERROR
    RX_DONE
    TIMEOUT
    */

    // uses bitwise AND so it can check multiple values in irq_status
    // for ex, SX126X_IRQ_TX_DONE is a mask
    if ((irq_status & SX126X_IRQ_TX_DONE) != 0U)
    {
        // this is good, we sent it
        lora_tx_in_progress = false;
        return LORA_EVENT_TX_DONE;
    }

    // if ((irq_status & SX126X_IRQ_CRC_ERROR) != 0U)
    // {
    //     return LORA_EVENT_CRC_ERROR;
    // }

    if ((irq_status &
        (SX126X_IRQ_CRC_ERROR | SX126X_IRQ_HEADER_ERROR)) != 0U)
    {
        return LORA_EVENT_CRC_ERROR;
    }

    if ((irq_status & SX126X_IRQ_RX_DONE) != 0U)
    {
        return LORA_EVENT_RX_DONE;
    }

    if ((irq_status & SX126X_IRQ_TIMEOUT) != 0U)
    {
        lora_tx_in_progress = false;
        return LORA_EVENT_TIMEOUT;
    }

    return LORA_EVENT_NONE;
}


bool LoRa_IsTxInProgress(void)
{
    return lora_tx_in_progress;
}

sx126x_status_t LoRa_AbortTx(void)
{
    sx126x_status_t status;

    lora_tx_in_progress = false;
    lora_dio1_pending = false;

    status = sx126x_set_standby(
        &radio,
        SX126X_STANDBY_CFG_RC);

    if (status != SX126X_STATUS_OK)
    {
        return status;
    }

    return sx126x_clear_irq_status(
        &radio,
        SX126X_IRQ_ALL);
}


// recieive code
/////////////////////////////////////////////////////////////////////////////


sx126x_status_t LoRa_StartReceive(void)
{
    sx126x_status_t status;

    if (radio.spi == NULL)
    {
        return SX126X_STATUS_ERROR;
    }

    lora_tx_in_progress = false;
    lora_dio1_pending = false;

    status = sx126x_set_standby(
        &radio,
        SX126X_STANDBY_CFG_RC);

    if (status != SX126X_STATUS_OK)
    {
        return status;
    }

    /*
     * Receiver settings must match the transmitter:
     *
     * Preamble:       8 symbols
     * Header:         explicit
     * CRC:            enabled
     * IQ inversion:   disabled
     *
     * In explicit-header mode, the packet header tells the receiver
     * the actual received payload length. Set 255 here to permit the
     * maximum SX1262 payload size.
     */
    sx126x_pkt_params_lora_t packet_params =
    {
        .preamble_len_in_symb = 8,
        .header_type          = SX126X_LORA_PKT_EXPLICIT,
        .pld_len_in_bytes     = 255,
        .crc_is_on            = true,
        .invert_iq_is_on      = false
    };

    status = sx126x_set_lora_pkt_params(
        &radio,
        &packet_params);

    if (status != SX126X_STATUS_OK)
    {
        return status;
    }

    status = sx126x_clear_irq_status(
        &radio,
        SX126X_IRQ_ALL);

    if (status != SX126X_STATUS_OK)
    {
        return status;
    }

    /*
     * Route the receive-related IRQs to DIO1.
     */
    const sx126x_irq_mask_t rx_irq_mask =
        SX126X_IRQ_RX_DONE      |
        SX126X_IRQ_CRC_ERROR    |
        SX126X_IRQ_HEADER_ERROR |
        SX126X_IRQ_TIMEOUT;

    status = sx126x_set_dio_irq_params(
        &radio,
        rx_irq_mask,        /* Enabled radio IRQs */
        rx_irq_mask,        /* IRQs routed to DIO1 */
        SX126X_IRQ_NONE,    /* Nothing on DIO2 */
        SX126X_IRQ_NONE);   /* Nothing on DIO3 */

    if (status != SX126X_STATUS_OK)
    {
        return status;
    }

    /*
     * SX126X_RX_CONTINUOUS is a special RTC-step value.
     * Do not pass it to sx126x_set_rx(), because that function expects
     * milliseconds and applies a normal timeout range check.
     */
    return sx126x_set_rx_with_timeout_in_rtc_step(
        &radio,
        SX126X_RX_CONTINUOUS);
}

sx126x_status_t LoRa_ReadReceivedPacket(
    uint8_t *payload,
    uint8_t payload_capacity,
    uint8_t *payload_length,
    sx126x_pkt_status_lora_t *packet_status)
{
    sx126x_status_t status;
    sx126x_rx_buffer_status_t buffer_status;

    if ((payload == NULL) ||
        (payload_length == NULL) ||
        (payload_capacity == 0U))
    {
        return SX126X_STATUS_ERROR;
    }

    *payload_length = 0U;

    /*
     * Ask the radio:
     * 1. How many bytes were received?
     * 2. Where does the received packet begin in the SX1262 buffer?
     */
    status = sx126x_get_rx_buffer_status(
        &radio,
        &buffer_status);

    if (status != SX126X_STATUS_OK)
    {
        return status;
    }

    uint8_t bytes_to_copy = buffer_status.pld_len_in_bytes;

    /*
     * Protect the caller's buffer.
     *
     * The reported length is changed to the amount actually copied.
     */
    if (bytes_to_copy > payload_capacity)
    {
        bytes_to_copy = payload_capacity;
    }

    status = sx126x_read_buffer(
        &radio,
        buffer_status.buffer_start_pointer,
        payload,
        bytes_to_copy);

    if (status != SX126X_STATUS_OK)
    {
        return status;
    }

    *payload_length = bytes_to_copy;

    if (packet_status != NULL)
    {
        status = sx126x_get_lora_pkt_status(
            &radio,
            packet_status);

        if (status != SX126X_STATUS_OK)
        {
            return status;
        }
    }

    return SX126X_STATUS_OK;
}

