//*****************************************************************************
//
// enet_lwip.c - Sample WebServer Application using lwIP.
//
// Copyright (c) 2013-2017 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 2.1.4.178 of the EK-TM4C1294XL Firmware Package.
//
//*****************************************************************************

//#include <pinoutDKh.sav>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/flash.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "utils/lwiplib.h"
#include "utils/ustdlib.h"
#include "utils/uartstdio.h"
#include "drivers/pinout.h"
#include "lwip/udp.h"
#include "lwip/debug.h"
#include "lwip/stats.h"
//#include "lwip/tcp.h"
#include "tcpio.h"
#include "platform.h"
#include "spi.h"
#include "adcUtil.h"
#include "pwmUtil.h"
#include "poe.h"

//*****************************************************************************
//
//! \addtogroup example_list
//! <h1>Ethernet with lwIP (enet_lwip)</h1>
//!
//! This example application demonstrates the operation of the Tiva
//! Ethernet controller using the lwIP TCP/IP Stack.  DHCP is used to obtain
//! an Ethernet address.  If DHCP times out without obtaining an address,
//! AutoIP will be used to obtain a link-local address.  The address that is
//! selected will be shown on the UART.
//!
//! UART0, connected to the ICDI virtual COM port and running at 115,200,
//! 8-N-1, is used to display messages from this application. Use the
//! following command to re-build the any file system files that change.
//!
//!     ../../../../tools/bin/makefsfile -i fs -o enet_fsdata.h -r -h -q
//!
//! For additional details on lwIP, refer to the lwIP web page at:
//! http://savannah.nongnu.org/projects/lwip/
//
//*****************************************************************************

//*****************************************************************************
//
// Defines for setting up the system clock.
//
//*****************************************************************************
#define SYSTICKHZ               100
#define SYSTICKMS               (1000 / SYSTICKHZ)

//*****************************************************************************
//
// Interrupt priority definitions.  The top 3 bits of these values are
// significant with lower values indicating higher priority interrupts.
//
//*****************************************************************************
#define SYSTICK_INT_PRIORITY    0x80
#define ETHERNET_INT_PRIORITY   0xC0


//*****************************************************************************
//
// Pod defines and strictures.
//
//*****************************************************************************

struct vm_settings VM;

//*****************************************************************************
//
// The current IP address.
//
//*****************************************************************************
uint32_t g_ui32IPAddress;

//*****************************************************************************
//
// The system clock frequency.
//
//*****************************************************************************
uint32_t g_ui32SysClock;

//*****************************************************************************
//
// Volatile global flag to manage LED blinking, since it is used in interrupt
// and main application.  The LED blinks at the rate of SYSTICKHZ.
//
//*****************************************************************************
volatile bool g_bLED;
volatile uint32_t g_100usTick = 0;
volatile unsigned int g_delayusTick = 0;

//*****************************************************************************
//
// The client IP address set when first TCPIP command received.
//
//*****************************************************************************
uint32_t _slotAddress;

char tcpIn[RECVBUFLEN];
char tcpOut[RECVBUFLEN];
char udpOut[SENDBUFLEN];
unsigned short g_usSeq = 0;

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif


struct udp_pcb *UDPinit( void );

//*****************************************************************************
//
// Display an lwIP type IP Address.
//
//*****************************************************************************
void DisplayIPAddress(uint32_t ui32Addr)
{
    char pcBuf[16];

    //
    // Convert the IP Address into a string.
    //
    usprintf(pcBuf, "%d.%d.%d.%d", ui32Addr & 0xff, (ui32Addr >> 8) & 0xff,
            (ui32Addr >> 16) & 0xff, (ui32Addr >> 24) & 0xff);

    // set the green LED on and red LED off.
    MAP_GPIOPinWrite(GPIO_PORTP_BASE, GPIO_PIN_4, GPIO_PIN_4);
    MAP_GPIOPinWrite(GPIO_PORTP_BASE, GPIO_PIN_5, ~GPIO_PIN_5);
}


//*****************************************************************************
//
// Required by lwIP library to support any host-related timer functions.
//
//*****************************************************************************
void lwIPHostTimerHandler(void)
{
    uint32_t ui32NewIPAddress;
    unsigned char mac[6];

    //
    // Get the current IP address.
    //
    ui32NewIPAddress = lwIPLocalIPAddrGet();
    lwIPLocalMACGet(mac);

    //
    // See if the IP address has changed.
    //
    if(ui32NewIPAddress != g_ui32IPAddress)
    {
        //
        // See if there is an IP address assigned.
        //
        if(ui32NewIPAddress == 0xffffffff)
        {
            //
            // Indicate that there is no link.
            //
//            printf("Waiting for link.\n");
        }
        else if(ui32NewIPAddress == 0)
        {
            //
            // There is no IP address, so indicate that the DHCP process is
            // running.
            //
//            printf("Waiting for IP address.\n");
        }
        else
        {
            //
            // Display the new IP address.
            //
//            printf("IP Address: ");
            DisplayIPAddress(ui32NewIPAddress);
//            printf("\nEcho Server is ready.\n");
        }

        //
        // Save the new IP address.
        //
        g_ui32IPAddress = ui32NewIPAddress;
    }

    //
    // If there is not an IP address.
    //
    if((ui32NewIPAddress == 0) || (ui32NewIPAddress == 0xffffffff))
    {
        //
        // Do nothing and keep waiting.
        //
    }
}

//*****************************************************************************
//
// The interrupt handler for the SysTick interrupt.
//
//*****************************************************************************
void SysTickIntHandler(void)
{
    //
    // Call the lwIP timer handler.
    //
    lwIPTimer(SYSTICKMS);

    //
    // Tell the application to change the state of the LED (in other words
    // blink).
    //
    if (g_100usTick > 0)
        g_100usTick--;

    g_delayusTick++;
}

//*****************************************************************************
//
// This example demonstrates the use of the Ethernet Controller.
//
//*****************************************************************************
int main(void)
{
    uint32_t ui32User0, ui32User1;
    uint8_t pui8MACArray[8];
    struct udp_pcb * UDPpcb;
    struct pbuf *p = 0;
    int change;

    //  IP4_ADDR(&ipAddr, 172, 16, 2, 138);

    //
    // Make sure the main oscillator is enabled because this is required by
    // the PHY.  The system must have a 25MHz crystal attached to the OSC
    // pins. The SYSCTL_MOSC_HIGHFREQ parameter is used when the crystal
    // frequency is 10MHz or higher.
    //
    SysCtlMOSCConfigSet(SYSCTL_MOSC_HIGHFREQ);

    //
    // Run from the PLL at 120 MHz.
    //
    g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                                             SYSCTL_OSC_MAIN |
                                             SYSCTL_USE_PLL |
                                             SYSCTL_CFG_VCO_480), 120000000);


    //
    // Configure the device pins.
    //
    PinoutSet();

    //
    // Configure SPI
    //
    SPI_init();

    //
    // Configure ADC
    //
    ADC_init();

    //
    // Configure PWM
    //
    PWM_init();

    //
    // Set the red LED on
    //
    MAP_GPIOPinWrite(GPIO_PORTP_BASE, GPIO_PIN_5, GPIO_PIN_5);

    //
    // Get switch address
    //
    _slotAddress = READ_MYADDR;

    //
    // initialize POD structure
    //
    VM.AnalogFilterCoef = 1;
    VM.DataStreamDelay = data_stream_delay_ms;
    switch (_slotAddress) {
    case 0:
        strcpy(VM.PodId, "PP1/EP");
        break;
    case 1:
        strcpy(VM.PodId, "PP2/K1");
        break;
    case 2:
        strcpy(VM.PodId, "PP3/K2");
        break;
    case 3:
        strcpy(VM.PodId, "PP4/BF");
        break;
    default:
        strcpy(VM.PodId, "PPx/NU");
        break;
    }

    MAP_SysTickPeriodSet(g_ui32SysClock / SYSTICKHZ);
//    MAP_SysTickPeriodSet(g_ui32SysClock / 10000);
    MAP_SysTickEnable();
    MAP_SysTickIntEnable();

    //
    // Configure the hardware MAC address for Ethernet Controller filtering of
    // incoming packets.  The MAC address will be stored in the non-volatile
    // USER0 and USER1 registers.
    //

    ui32User0 = 0x002B0D00;     // Astronics MAC Address prefix 00:0D:2B
    ui32User1 = 0x00EA0200 + _slotAddress;

    //
    // Convert the 24/24 split MAC address from NV ram into a 32/16 split MAC
    // address needed to program the hardware registers, then program the MAC
    // address into the Ethernet Controller registers.
    //
    pui8MACArray[0] = ((ui32User0 >>  0) & 0xff);
    pui8MACArray[1] = ((ui32User0 >>  8) & 0xff);
    pui8MACArray[2] = ((ui32User0 >> 16) & 0xff);
    pui8MACArray[3] = ((ui32User1 >>  0) & 0xff);
    pui8MACArray[4] = ((ui32User1 >>  8) & 0xff);
    pui8MACArray[5] = ((ui32User1 >> 16) & 0xff);

    if (STATIC_IP) {
        struct ip_addr ipAddr;
        struct ip_addr subnet;
        struct ip_addr gateway;

        // initialize ip, subnet and gateway
        ipAddr.addr = (10u << 24) | (0u << 16) | (0u << 8) | _slotAddress;
        subnet.addr = (255u << 24) | (255u << 16) | (0u << 8) | 0;
        gateway.addr = (0u << 24) | (0u << 16) | (0u << 8) | 0;
//        IP4_ADDR(&ipAddr, 10, 0, 0, _slotAddress);
//        IP4_ADDR(&subnet, 255, 255, 0, 0);
//        IP4_ADDR(&gateway, 0, 0, 0, 0);

        //
        // Initialize the lwIP library, using static.
        //
        lwIPInit(g_ui32SysClock, pui8MACArray, ipAddr.addr, subnet.addr, gateway.addr, IPADDR_USE_STATIC);
    } else {
        //
        // Initialize the lwIP library, using DHCP.
        //
        lwIPInit(g_ui32SysClock, pui8MACArray, 0, 0, 0, IPADDR_USE_DHCP);
    }

    clientIpAddr.addr = 0;

    //
    // Initialize the UDP socket.
    //
    UDPpcb = UDPinit();

    //
    // Initialize the TCP socket.
    //
    TCPinit();

    //
    // Set the interrupt priorities.  We set the SysTick interrupt to a higher
    // priority than the Ethernet interrupt to ensure that the file system
    // tick is processed if SysTick occurs while the Ethernet handler is being
    // processed.  This is very likely since all the TCP/IP and HTTP work is
    // done in the context of the Ethernet interrupt.
    //
    MAP_IntPrioritySet(INT_EMAC0, ETHERNET_INT_PRIORITY);
    MAP_IntPrioritySet(FAULT_SYSTICK, SYSTICK_INT_PRIORITY);

    //
    // Reset the module
    //
    SetReset();

    //
    // Loop forever, processing the LED blinking.  All the work is done in
    // interrupt handlers.
    //

    while(1)
    {
        // get latest input
        change = updateUdpPacket(udpOut);

        /* can't send UDP untill TCP connected */
        if (clientIpAddr.addr != 0) {
            // send UPD if data delay expires or input changed state
            if (change || g_100usTick == 0) {
                // Toggle the red LED.
                MAP_GPIOPinWrite(GPIO_PORTP_BASE, GPIO_PIN_5,
                                 (MAP_GPIOPinRead(GPIO_PORTP_BASE, GPIO_PIN_5) ^
                                  GPIO_PIN_5));

                // only output UDP if data delay is enabled (-1 is disabled)
                if (data_stream_delay_ms >= 0) {
                    // allocate new pbuf
                    p=pbuf_alloc(PBUF_TRANSPORT, 32, PBUF_RAM);

                    if (p) {
                        sprintf(p->payload, "%s %04X %s\n", VM.PodId, g_usSeq, udpOut);

                        // increment the UDP number
                        g_usSeq++;

                        // send the UDP message
                        udp_sendto(UDPpcb, p, &clientIpAddr, UDP_PORT);

                        /* free the pbuf */
                        pbuf_free(p);
                    }
                }

                // reset the data delay counter
                VM.DataStreamDelay = data_stream_delay_ms;  // convert to float
                g_100usTick = VM.DataStreamDelay / 0.1;     // set data delay counter to 100us ticks
            }
        }
   }
}

struct udp_pcb *UDPinit(void)
{
    struct udp_pcb * pcb;

    /* get new pcb */
    pcb = udp_new();
    if (pcb == NULL) {
        LWIP_DEBUGF(UDP_DEBUG, ("udp_new failed!\n"));
        return 0;
    }

    /* bind to any IP address on UDP port */
    if (udp_bind(pcb, IP_ADDR_ANY, UDP_PORT) != ERR_OK) {
        LWIP_DEBUGF(UDP_DEBUG, ("udp_bind failed!\n"));
        return 0;
    }

    return pcb;
}

