
//////////////////////////////////////////////////////
// First things first, all the includes we need     //
//////////////////////////////////////////////////////

#include <cpu/irq.h>        // Interrupt functionality from BertOS

#include <drv/ser.h>        // Serial driver from BertOS
#include <drv/timer.h>      // Timer driver from BertOS

#include <stdio.h>          // Standard input/output
#include <string.h>         // String operations
#define F_CPU 16000000UL
#include <util/delay.h>

#include <net/ax25.h>

#include "afsk.h"           // Header for AFSK modem


#include "protocol/SimpleSerial.h"   // Simple serial control protocol
#include "protocol/KISS.h"           // KISS TNC protocol

#if SERIAL_DEBUG
    #include "cfg/debug.h"  // Debug configuration from BertOS
#endif

#define SERIAL_PROTOCOL PROTOCOL_SIMPLE_SERIAL

//////////////////////////////////////////////////////
// A few definitions                                //
//////////////////////////////////////////////////////

static Afsk afsk;           // Declare a AFSK modem struct
static AX25Ctx ax25;        // Declare a protocol struct
static Serial ser;          // Declare a serial interface struct

#define ADC_CH 0            // Define which channel (pin) we want
                            // for the ADC (this is A0 on arduino)


/* Removed for now, use serial to send packets instead
static AX25Call path[] = AX25_PATH(AX25_CALL(TO_CALL, 0), AX25_CALL(YOUR_CALLSIGN, 0), AX25_CALL("wide1", 1), AX25_CALL("wide2", 2));
#define SEND_TEST_PACKETS false
#define TEST_INTERVAL 15000L
#define APRS_MSG    "Test APRS packet"
*/

static uint8_t serialBuffer[CONFIG_AX25_FRAME_BUF_LEN+1]; // Buffer for holding incoming serial data
static int sbyte;                               // For holding byte read from serial port
static size_t serialLen = 0;                    // Counter for counting length of data from serial
static bool sertx = false;                      // Flag signifying whether it's time to send data
                                                // received on the serial port.

#define SER_BUFFER_FULL (serialLen < MP1_MAX_DATA_SIZE-1)



//////////////////////////////////////////////////////
// And here comes the actual program :)             //
//////////////////////////////////////////////////////

// This is a callback we register with the protocol,
// so we can process each packet as they are decoded.
// Right now it just prints the packet to the serial port.
static void message_callback(struct AX25Msg *msg)
{
    if (SERIAL_PROTOCOL == PROTOCOL_SIMPLE_SERIAL) {
        ss_messageCallback(msg, &ser);
    }
    if (SERIAL_PROTOCOL == PROTOCOL_KISS) {
        // Not implemented yet
    }
}

// Simple initialization function.
static void init(void)
{
    // Enable interrupts
    IRQ_ENABLE;

    // Initialize hardware timers
    timer_init();

    // Initialize serial comms on UART0,
    // which is the hardware serial on arduino
    ser_init(&ser, SER_UART0);
    ser_setbaudrate(&ser, 9600);

    // For some reason BertOS sets the serial
    // to 7 bit characters by default. We set
    // it to 8 instead.
    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);

    // Create a modem context
    afsk_init(&afsk, ADC_CH);
    // ... and a protocol context with the modem
    ax25_init(&ax25, &afsk.fd, message_callback);

    // Init SimpleSerial
    ss_init(&ax25);

    // That's all!
}

int main(void)
{
    // Start by running the main initialization
    init();
    // Record the current tick count for time-keeping
    ticks_t start = timer_clock();
    
    // Go into ye good ol' infinite loop
    while (1)
    {    
        // First we instruct the protocol to check for
        // incoming data
        ax25_poll(&ax25);

        // Poll for incoming serial data
        if (!sertx && ser_available(&ser)) {
            // We then read a byte from the serial port.
            // Notice that we use "_nowait" since we can't
            // have this blocking execution until a byte
            // comes in.
            sbyte = ser_getchar_nowait(&ser);

            // If SERIAL_DEBUG is specified we'll handle
            // serial data as direct human input and only
            // transmit when we get a LF character
            #if SERIAL_DEBUG
                // If we have not yet surpassed the maximum frame length
                // and the byte is not a "transmit" (newline) character,
                // we should store it for transmission.
                if ((serialLen < MP1_MAX_DATA_SIZE) && (sbyte != 10)) {
                    // Put the read byte into the buffer;
                    serialBuffer[serialLen] = sbyte;
                    // Increment the read length counter
                    serialLen++;
                } else {
                    // If one of the above conditions were actually the
                    // case, it means we have to transmit, se we set
                    // transmission flag to true.
                    sertx = true;
                }
            #else
                // Otherwise we assume the modem is running
                // in automated mode, and we push out data
                // as it becomes available. We either transmit
                // immediately when the max frame length has
                // been reached, or when we get no input for
                // a certain amount of time.

                if (serialLen < CONFIG_AX25_FRAME_BUF_LEN-1) {
                    // Put the read byte into the buffer;
                    serialBuffer[serialLen] = sbyte;
                    // Increment the read length counter
                    serialLen++;
                } else {
                    // If max frame length has been reached
                    // we need to transmit.
                    serialBuffer[serialLen] = sbyte;
                    serialLen++;
                    sertx = true;
                }

                start = timer_clock();
            #endif
        } else {
            if (!SERIAL_DEBUG && serialLen > 0 && timer_clock() - start > ms_to_ticks(TX_MAXWAIT)) {
                sertx = true;
            }
        }

        if (sertx) {
            ss_serialCallback(serialBuffer, serialLen, &ser, &ax25);
            sertx = false;
            serialLen = 0;
        }

        // Removing this for now
        /*
        // Use AX.25 to send test data
        if (SEND_TEST_PACKETS && timer_clock() - start > ms_to_ticks(TEST_INTERVAL))
        {
            start = timer_clock();
            ax25_sendVia(&ax25, path, countof(path), APRS_MSG, sizeof(APRS_MSG));
        }
        */

    }
    return 0;
}