/*
 * platform.c
 *
 *  Created on: Sep 13, 2018
 *      Author: robert.applebee
 */

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/rom_map.h"
#include "driverlib/flash.h"
#include "driverlib/interrupt.h"
#include "platform.h"
#include "driverlib/gpio.h"
#include "poe.h"
#include "spi.h"
#include "adcUtil.h"
#include "pwmUtil.h"

const long DATA_STREAM_DELAY_MIN = -1; // -1 = OFF, 0 = no delay
const long DATA_STREAM_DELAY_MAX = 3600000; // 1 hour in ms.

const long DATA_DIGITAL_CHAN_MIN = 0;       // minimum digital channel number
const long DATA_DIGITAL_CHAN_MAX = 15;      // maximum digital channel number
const long DATA_ANALOG_CHAN_MIN = 16;       // minimum analog channel number
const long DATA_ANALOG_CHAN_MAX = 17;       // maximum analog channel number

const int ANALOG_THRESHOLD_MIN = 0;
const int ANALOG_THRESHOLD_MAX = 0x0FFF; // 12 bits

//
// POE has 18 channels.
//  Channel 0-15 are I/O digital but digital level is read by ADC and compared to analog_thresholds.
//  Channel 16 and 17 are 0 to 10V I/O analog.
//
static char ioconfig[] = "------------------"; // NOTE this is null-terminated and so therefore 19 chars
static char Doutput[] = "----------------"; // NOTE this is null-terminated and so therefore 17 chars
static char Dinputs[] = "----------------"; // NOTE this is null-terminated and so therefore 17 chars
static int Aoutput[] = { 0, 0};
static char omode[] = "ssss"; // NOTE this is null-terminated and so therefore 5 chars
static char iimpedance[] = "zzzzzzzzzzzzzzzzzz"; // NOTE this is null-terminated and so therefore 19 chars
static char tainputs[] = "--"; // NOTE this is null-terminated and so therefore 3 chars
const int NUM_IO_PORTS = (sizeof(ioconfig) - 1) / sizeof(char);
const int NUM_DIGITAL_PORTS = (sizeof(Doutput) - 1) / sizeof(char);
const int NUM_ANALOG_PORTS = (sizeof(tainputs) - 1) / sizeof(char);
const int NUM_IO_GROUPS = 4;

static int analog_thresholds[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
static int pwr24Enabled;
const int NUM_ADC_PORTS = sizeof(analog_thresholds) / sizeof(int);

long data_stream_delay_ms = -1; // -1 = OFF, 0 = no delay //TODO change to 0?

#define TRUE 1
#define FALSE 0

//TODO move these to main or somewhere and extern
char* GetPodId() { return VM.PodId; }
char* GetFirmwareVersion() { return POE_VERSION; }

int updateUdpPacket(char *buf)
{
    char *ioc = ioconfig;
    char *bp;
    char c;
    char r;
    int change = FALSE;
    int i;
    double voltage;
    double current;

    ADC_get24v(&voltage, &current);

    if (voltage < 22.0 || current > 0.350) {
        // shut off +24 after 10 consecutive voltage or current error
        if (pwr24Enabled == 1) {
            //
            // disable the 24V
            //
            MAP_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_4, 0);
            pwr24Enabled = FALSE;
            strcpy (buf, "xxxxxxxxxxxxxxxx xx");
        } else if (pwr24Enabled) {
            // decrement count
            pwr24Enabled--;
        }
    } else {
        // set 24V power test counter to 1
        pwr24Enabled = 1;

        for (bp = buf, i = 0; i < NUM_ADC_PORTS; i++, bp++) {
            // get ioconfig
            c = *ioc++;

            // set responce character
            if (c != '-' && analog_thresholds[i] > 0)
                r = (adcResults[i] <= analog_thresholds[i])? '0': '1';
            else
                r = '-';

            // skip space in packet
            if (*bp == ' ')
                bp++;

            // set change flag
            c = *bp;
            if (r != c) {
                change = TRUE;
                *bp = r;
            }
        }
    }

    return change;
}

int SetReset(void)
{
    int channel;

    // turn off UDP
    data_stream_delay_ms = -1;

    // set digital channels off
    SPI_write(0x0);

    // set analog channels HiZ
    MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_2, 0);
    MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_3, 0);

    // set IO mode
    for (channel = 0; channel < NUM_IO_GROUPS; channel++)
        SetOutputMode(channel, 's');

    // reset io channels
    for (channel = 0; channel < NUM_IO_PORTS; channel++) {
        // set ioconfig
        SetIoConfig(channel, '-');

        // set input impedance to HiZ
        SetInputImpedance(channel, 'z');

        // set analog thresholds
        analog_thresholds[channel] = -1;
    }

    // reset digital arrays
    for (channel = 0; channel < NUM_DIGITAL_PORTS; channel++) {
        Doutput[channel] = '-';
        Dinputs[channel] = '-';
    }

    // reset analog arrays
    for (channel = 0; channel < NUM_ANALOG_PORTS; channel++) {
        tainputs[channel] = '-';
        Aoutput[channel] = 0;
    }

    // Set analog outputs to 0V
    PWM_set(16, 0);
    PWM_set(17, 0);

    //
    // Enable the 24V
    //
    MAP_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_PIN_4);

    // set number of retry counts to allow +24V to ramp up
    pwr24Enabled = 2000;

    return 0;
}


void SetIoConfig(int channelIndex, char state)
{
    // disable driver if channel is currently an output and now an input
    if (channelIndex < NUM_DIGITAL_PORTS) {
        if (state == 'i' && ChannelIsOutput(channelIndex)) {
            SetDigitalOutput(channelIndex, '1');
            UpdateDigitalOutput();
        }
    } else if (channelIndex == 16)
        MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_2, (state == 'o')? GPIO_PIN_2: 0);
    else
        MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_3, (state == 'o')? GPIO_PIN_3: 0);

    ioconfig[channelIndex] = state;
}

char GetIoConfig(int channelIndex)
{
    return ioconfig[channelIndex];
}

char * GetIoConfigAll(void)
{
    return ioconfig;
}

int ChannelIsOutput(int channelIndex) { return ioconfig[channelIndex] == 'o'; }


void SetOutputMode(int channelIndex, char state)
{
    int io0;

    // set string
    omode[channelIndex] = state;

    // set GPIO
    switch(state) {
    case 's':
        io0 = 0;
        break;
    default:
        io0 = 1;
        break;
    }
    switch (channelIndex)
    {
    case 0:
        MAP_GPIOPinWrite(GPIO_PORTP_BASE, GPIO_PIN_0, io0);
        break;
    case 1:
        MAP_GPIOPinWrite(GPIO_PORTP_BASE, GPIO_PIN_1, io0 << 1);
        break;
    case 2:
        MAP_GPIOPinWrite(GPIO_PORTP_BASE, GPIO_PIN_2, io0 << 2);
        break;
    default:
        MAP_GPIOPinWrite(GPIO_PORTP_BASE, GPIO_PIN_3, io0 << 3);
        break;
    }
}

char GetOutputMode(int channelIndex)
{
    return omode[channelIndex];
}

void SetInputImpedance(int channelIndex, char state)
{
    int io0, io1;

    // set string
    iimpedance[channelIndex] = state;

    // set GPIO
    switch(state) {
    case 'u':
        io0 = 1;
        io1 = 0;
        break;
    case 'd':
        io0 = 0;
        io1 = 0;
        break;
    default:
        io0 = 0;
        io1 = 1;
        break;
    }

    switch (channelIndex)
    {
    case 0:
        MAP_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_0, io0);
        MAP_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_1, io1 << 1);
        break;
    case 1:
        MAP_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_2, io0 << 2);
        MAP_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_3, io1 << 3);
        break;
    case 2:
        MAP_GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_4, io0 << 4);
        MAP_GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_5, io1 << 5);
        break;
    case 3:
        MAP_GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_6, io0 << 6);
        MAP_GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_7, io1 << 7);
        break;
    case 4:
        MAP_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, io0 << 2);
        MAP_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, io1 << 3);
        break;
    case 5:
        MAP_GPIOPinWrite(GPIO_PORTG_BASE, GPIO_PIN_0, io0);
        MAP_GPIOPinWrite(GPIO_PORTG_BASE, GPIO_PIN_1, io1 << 1);
        break;
    case 6:
        MAP_GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, io0);
        MAP_GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_1, io1 << 1);
        break;
    case 7:
        MAP_GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_2, io0 << 2);
        MAP_GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_3, io1 << 3);
        break;
    case 8:
        MAP_GPIOPinWrite(GPIO_PORTJ_BASE, GPIO_PIN_0, io0);
        MAP_GPIOPinWrite(GPIO_PORTJ_BASE, GPIO_PIN_1, io1 << 1);
        break;
    case 9:
        MAP_GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_6, io0 << 6);
        MAP_GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_7, io1 << 7);
        break;
    case 10:
        MAP_GPIOPinWrite(GPIO_PORTL_BASE, GPIO_PIN_2, io0 << 2);
        MAP_GPIOPinWrite(GPIO_PORTL_BASE, GPIO_PIN_3, io1 << 3);
        break;
    case 11:
        MAP_GPIOPinWrite(GPIO_PORTL_BASE, GPIO_PIN_6, io0 << 6);
        MAP_GPIOPinWrite(GPIO_PORTL_BASE, GPIO_PIN_7, io1 << 7);
        break;
    case 12:
        MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_0, io0);
        MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_1, io1 << 1);
        break;
    case 13:
        MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_2, io0 << 2);
        MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_3, io1 << 3);
        break;
    case 14:
        MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_4, io0 << 4);
        MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_5, io1 << 5);
        break;
    case 15:
        MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_6, io0 << 6);
        MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_7, io1 << 7);
        break;
    case 16:
        MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, io1);
        break;
    default:
        MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, io1 << 1);
        break;
    }
}

char *GetInputImpedanceAll(void)
{
    return iimpedance;
}

// does notihing if not configured for output, but you shouldn't call this if it isn't
void SetDigitalOutput(int channelIndex, int binaryValue)
{
    if (ChannelIsOutput(channelIndex))
        Doutput[channelIndex] = (binaryValue ? '1' : '0');

}

// returns 0 if not configured for output, but you shouldn't call this if it isn't
int GetDigitalOutput(int channelIndex) { return (Doutput[channelIndex] == '1'); }

char * GetDigitalOutputAll()
{
    return Doutput;
}

void UpdateDigitalOutput(void)
{
    unsigned short dOutput = 0;
    char *c = Doutput;
    int channel;

    for (channel = 0; channel < NUM_DIGITAL_PORTS; channel++)
    {
        if (*c++ == '0')
            dOutput |= (1 << channel);
    }

    SPI_write(dOutput);
}

void SetAnalogThreshold(int channelIndex, int value)
{
    analog_thresholds[channelIndex] = value;
}

int GetAnalogThreshold(int channelIndex)
{
    return analog_thresholds[channelIndex];
}

void SetAnalogOutput(int channelIndex, int value)
{
    Aoutput[channelIndex - 16] = value;
    PWM_set(channelIndex, value);
}

int GetAnalogOutput(int channelIndex)
{
    return Aoutput[channelIndex - 16];
}

int GetAnalogInput(int channelIndex)
{
    return adcResults[channelIndex];
}

char * GetDigitalInputsAll()
{
    return Dinputs;
}

char * GetThresholdedAnalogInputsAll()
{
    return tainputs;
}


void SetDataStreamDelay(long value)
{
    data_stream_delay_ms = value;
}

extern long GetDataStreamDelay()
{
    return data_stream_delay_ms;
}

void SetAnalogFilterCoef(long value)
{
    VM.AnalogFilterCoef = value;
}

extern long GetAnalogFilterCoef()
{
    return VM.AnalogFilterCoef;
}







