/*
Copyright (c) 2013, SEMTECH S.A.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the Semtech corporation nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Description: Ping-Pong implementation.  Adapted to run in the MyNewt OS.
*/

#include <string.h>
#include "os/mynewt.h"
#include "hal/hal_gpio.h"
#include "hal/hal_spi.h"
#include "bsp/bsp.h"
#include "radio/radio.h"
#include "loraping.h"

#define USE_BAND_915

#if defined(USE_BAND_433)

#define RF_FREQUENCY               434000000 /* Hz */

#elif defined(USE_BAND_780)

#define RF_FREQUENCY               780000000 /* Hz */

#elif defined(USE_BAND_868)

#define RF_FREQUENCY               868000000 /* Hz */

#elif defined(USE_BAND_915)

#define RF_FREQUENCY               915000000 /* Hz */

#else
    #error "Please define a frequency band in the compiler options."
#endif

#define LORAPING_TX_OUTPUT_POWER            14        /* dBm */

#define LORAPING_BANDWIDTH                  0         /* [0: 125 kHz, */
                                                  /*  1: 250 kHz, */
                                                  /*  2: 500 kHz, */
                                                  /*  3: Reserved] */
#define LORAPING_SPREADING_FACTOR           7         /* [SF7..SF12] */
#define LORAPING_CODINGRATE                 1         /* [1: 4/5, */
                                                  /*  2: 4/6, */
                                                  /*  3: 4/7, */
                                                  /*  4: 4/8] */
#define LORAPING_PREAMBLE_LENGTH            8         /* Same for Tx and Rx */
#define LORAPING_SYMBOL_TIMEOUT             5         /* Symbols */
#define LORAPING_FIX_LENGTH_PAYLOAD_ON      false
#define LORAPING_IQ_INVERSION_ON            false

#define LORAPING_TX_TIMEOUT_MS              3000    /* ms */
#define LORAPING_RX_TIMEOUT_MS              1000    /* ms */
#define LORAPING_BUFFER_SIZE                64

const uint8_t loraping_ping_msg[] = "PING";
const uint8_t loraping_pong_msg[] = "PONG";

static uint8_t loraping_buffer[LORAPING_BUFFER_SIZE];
static int loraping_rx_size;
static int loraping_is_master = 1;

struct {
    int rx_timeout;
    int rx_ping;
    int rx_pong;
    int rx_other;
    int rx_error;
    int tx_timeout;
    int tx_success;
} loraping_stats;

static void loraping_tx(struct os_event *ev);
static void loraping_rx(struct os_event *ev);

static struct os_event loraping_ev_tx = {
    .ev_cb = loraping_tx,
};
static struct os_event loraping_ev_rx = {
    .ev_cb = loraping_rx,
};

static void
send_once(int is_ping)
{
    int i;

    if (is_ping) {
        memcpy(loraping_buffer, loraping_ping_msg, 4);
    } else {
        memcpy(loraping_buffer, loraping_pong_msg, 4);
    }
    for (i = 4; i < sizeof loraping_buffer; i++) {
        loraping_buffer[i] = i - 4;
    }

    Radio.Send(loraping_buffer, sizeof loraping_buffer);
}

static void
loraping_tx(struct os_event *ev)
{
    /* Print information about last rx attempt. */
    loraping_rxinfo_print();

    if (loraping_rx_size == 0) {
        /* Timeout. */
    } else {
        os_time_delay(1);
        if (memcmp(loraping_buffer, loraping_pong_msg, 4) == 0) {
            loraping_stats.rx_ping++;
        } else if (memcmp(loraping_buffer, loraping_ping_msg, 4) == 0) {
            loraping_stats.rx_pong++;

            /* A master already exists.  Become a slave. */
            loraping_is_master = 0;
        } else {
            /* Valid reception but neither a PING nor a PONG message. */
            loraping_stats.rx_other++;
            /* Set device as master and start again. */
            loraping_is_master = 1;
        }
    }

    loraping_rx_size = 0;
    send_once(loraping_is_master);
}

static void
loraping_rx(struct os_event *ev)
{
    Radio.Rx(LORAPING_RX_TIMEOUT_MS);
}

static void
on_tx_done(void)
{
    loraping_stats.tx_success++;
    Radio.Sleep();

    os_eventq_put(os_eventq_dflt_get(), &loraping_ev_rx);
}

static void
on_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
{
    Radio.Sleep();

    if (size > sizeof loraping_buffer) {
        size = sizeof loraping_buffer;
    }

    loraping_rx_size = size;
    memcpy(loraping_buffer, payload, size);

    loraping_rxinfo_rxed(rssi, snr);

    os_eventq_put(os_eventq_dflt_get(), &loraping_ev_tx);
}

static void
on_tx_timeout(void)
{
    Radio.Sleep();

    loraping_stats.tx_timeout++;

    os_eventq_put(os_eventq_dflt_get(), &loraping_ev_rx);
}

static void
on_rx_timeout(void)
{
    Radio.Sleep();

    loraping_stats.rx_timeout++;
    loraping_rxinfo_timeout();

    os_eventq_put(os_eventq_dflt_get(), &loraping_ev_tx);
}

static void
on_rx_error(void)
{
    loraping_stats.rx_error++;
    Radio.Sleep();

    os_eventq_put(os_eventq_dflt_get(), &loraping_ev_tx);
}

int
mynewt_main(int argc, char **argv)
{
    RadioEvents_t radio_events;

    sysinit();

    /* Radio initialization. */
    radio_events.TxDone = on_tx_done;
    radio_events.RxDone = on_rx_done;
    radio_events.TxTimeout = on_tx_timeout;
    radio_events.RxTimeout = on_rx_timeout;
    radio_events.RxError = on_rx_error;

    Radio.Init(&radio_events);

    Radio.SetChannel(RF_FREQUENCY);

    Radio.SetTxConfig(MODEM_LORA,
                      LORAPING_TX_OUTPUT_POWER,
                      0,        /* Frequency deviation; unused with LoRa. */
                      LORAPING_BANDWIDTH,
                      LORAPING_SPREADING_FACTOR,
                      LORAPING_CODINGRATE,
                      LORAPING_PREAMBLE_LENGTH,
                      LORAPING_FIX_LENGTH_PAYLOAD_ON,
                      true,     /* CRC enabled. */
                      0,        /* Frequency hopping disabled. */
                      0,        /* Hop period; N/A. */
                      LORAPING_IQ_INVERSION_ON,
                      LORAPING_TX_TIMEOUT_MS);

    Radio.SetRxConfig(MODEM_LORA,
                      LORAPING_BANDWIDTH,
                      LORAPING_SPREADING_FACTOR,
                      LORAPING_CODINGRATE,
                      0,        /* AFC bandwisth; unused with LoRa. */
                      LORAPING_PREAMBLE_LENGTH,
                      LORAPING_SYMBOL_TIMEOUT,
                      LORAPING_FIX_LENGTH_PAYLOAD_ON,
                      0,        /* Fixed payload length; N/A. */
                      true,     /* CRC enabled. */
                      0,        /* Frequency hopping disabled. */
                      0,        /* Hop period; N/A. */
                      LORAPING_IQ_INVERSION_ON,
                      true);    /* Continuous receive mode. */

    /* Immediately receive on start up. */
    os_eventq_put(os_eventq_dflt_get(), &loraping_ev_rx);

    /*
     * As the last thing, process events from default event queue.
     */
    while (1) {
        os_eventq_run(os_eventq_dflt_get());
    }
}
