/*
 * my_ethernet.c
 *
 *  Created on: gg/mmm/ccaa
 *      Author: cognome.nome
 *
 * funzioni per la ethernet
 *
 */

//==============================================================================
// Include files

#include "stdbool.h"
#include "stdint.h"
#include "string.h"
#include "stdio.h"

#include "inc/hw_memmap.h"
#include "inc/hw_ints.h"

#include "driverlib/interrupt.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/pin_map.h"
#include "driverlib/gpio.h"
#include "drivers/my_LCD.h"

#include "utils/lwiplib.h"
#include "utils/locator.h"

#include "main.h"
#include "drivers/AD7193.h"
#include "my_flash.h"
#include "my_timer.h"
#include "drivers/my_USB.h"

//==============================================================================
// Constants

#define ADU_MAX_LENGTH 260
#define  MAX_TABLE  250

//==============================================================================
// Types

typedef struct
{   //mBAP Header
	uint16_t Trans_ID;
	uint16_t Protocol_ID;  // Modbus =0
	uint16_t len;
	uint8_t  SlaveAddress;
	uint8_t  Funct;
	char  rxdata[ADU_MAX_LENGTH];
	char txdata[ADU_MAX_LENGTH];
}modb;

//==============================================================================
// Static global variables

static struct tcp_pcb *pcb;
static char tcp_rx[512];
static char tcp_tx[512];
static uint16_t input_table   [MAX_TABLE];
static uint16_t holding_table [MAX_TABLE];

//==============================================================================
// Static functions

static void DisplayIPAddress(uint32_t ui32Addr,uint8_t linea)
{
    char pcBuf[16];
    // Questo formato è richiesto dalle lwip
    sprintf(pcBuf, " IP: %d.%d.%d.%d", ui32Addr & 0xff, (ui32Addr >> 8) & 0xff,
                (ui32Addr >> 16) & 0xff, (ui32Addr >> 24) & 0xff);
    LCD_WriteLine(linea,pcBuf);
}

// converte 2 bytes nella stringa ricevuta in word
static uint16_t c2w(char*str, uint8_t p)
{
	uint16_t aux;
	aux=(str[p]>>8)+str[p+1];
	return aux;
}

// converte 2 bytes nella stringa ricevuta in word
static void w2c (char*str, uint8_t p,uint16_t w)
{

	str[p]=(uint8_t) w>>8;
	str[p+1]=(uint8_t) w & 0XFF;
}

/*
static int16_t close_conn(struct tcp_pcb *pcb)
{


	      tcp_arg(pcb,NULL);
      	  tcp_sent(pcb,NULL);
      	  tcp_recv(pcb,NULL);
      	  tcp_close(pcb);
      	  sprintf(MSG,"Connessione chiusa  ");
      	  LCD_WriteLine(2,MSG);
          return (0);
}
*/

static int16_t send_mb_answer(struct tcp_pcb *pcb, char* tx)
{
	 uint16_t l;
	 l=c2w(tx,4)+6;
	 tcp_write(pcb, tx, l, 0);
	 tcp_sent(pcb, NULL);
	 return(0);
}

static void modb_parser(char* rx,char* tx, uint16_t len)
{
	 uint8_t len_pack;
     uint16_t i;
     uint16_t start;
     uint16_t Nreg;
     uint16_t value;
     modb  mb;
// carica la struttura modbus.
     mb.Trans_ID=c2w(rx,0);
     mb.Protocol_ID=c2w(rx,2);
     mb.len=c2w(rx,4);
     mb.SlaveAddress=rx[6];

	 for(i=0;i<mb.len;i++)
	 {
		 mb.rxdata[i]=rx[i+7];
	 }
// dati del modbus il registro 0 è la funzione
	 switch(mb.rxdata[0])  // function
  	 {
	 	 case 3:
	     case 4:
    		start=c2w(mb.rxdata,1)-1; // primo dato registro i registri partono da 0
    		Nreg=c2w(mb.rxdata,3);
    		if(Nreg<0x7D)
    		{
    			if(start<MAX_TABLE && start+Nreg<MAX_TABLE  ) // gestione dati
    			{
    				mb.txdata[0]=mb.rxdata[0];
    				mb.txdata[1]=Nreg*2;
    				for(i=0;i<Nreg;i++)
		            {
    					if(mb.rxdata[0]==3) w2c (mb.txdata,2+i*2,holding_table[start+i]); // scrive la stringa corretta nei dati
    					else w2c (mb.txdata,2+i*2,input_table[start+i]); // se la funzione è 3  legge la holding table 4 la input table
		            }
		            len_pack=mb.txdata[1]+2;
    			}
    			else //errore la richiesta esce dalle dimensioni della tabella
    			{
    				mb.txdata[0]=mb.rxdata[0]+0x80;
    				mb.txdata[1]=0x2;
    				len_pack=2;
    			}
    		}
    		else  //errore non possono essere inviati più di 125 dati
    		{
 			  	mb.txdata[0]=mb.rxdata[0]+0x80;
 			   	mb.txdata[1]=0x3;
 			   len_pack=2;
    	    }

     break;

	 case 6:  // scrive il singolo registro
		 start=c2w(mb.rxdata,1)-1; // primo dato registro i registri partono da 0
		 value=c2w(mb.rxdata,3);  // i valori sono sempre a 16 bit quindi non c'è l eccezione 3
 		 if(start<MAX_TABLE )  // sono nella dimensione della tabella di Holding
 		 {
			mb.txdata[0]=mb.rxdata[0];
		    w2c (mb.txdata,1,start);
		    w2c (mb.txdata,3,value);
		    holding_table[start]=value;
 		}
 		else //errore la richiesta esce dalle dimensioni della tabella
 		{
 				mb.txdata[0]=mb.rxdata[0]+0x80;
 				mb.txdata[1]=0x2;
 				len_pack=2;
 		}
 	 break;

	 case 16:
	    		start=c2w(mb.rxdata,1)-1; // primo dato registro i registri partono da 0
	    		Nreg=c2w(mb.rxdata,3);    //
	    		len_pack=mb.rxdata[5];
	    		if(Nreg<0x7D)
	    		{
	    			if(start<MAX_TABLE && start+Nreg<MAX_TABLE  ) // gestione dati
	    			{
	    				mb.txdata[0]=mb.rxdata[0];  //trasferisco il function code
	    				w2c (mb.txdata,1,start+1);  // trasferisco lo starting address
	    				w2c (mb.txdata,3,Nreg);  // trasferisco lo starting address
	    				len_pack+=1;

	    				for(i=0;i<Nreg;i++)
			            {
	    					holding_table[start+i] =c2w(mb.rxdata,6+2*i);
			            }
	    			}
	    			else //errore la richiesta esce dalle dimensioni della tabella
	    			{
	    				mb.txdata[0]=mb.rxdata[0]+0x80;
	    				mb.txdata[1]=0x2;
	    				len_pack=2;
	    			}
	    		}
	    		else  //errore non possono essere inviati più di 125 dati
	    		{
	 			  	mb.txdata[0]=mb.rxdata[0]+0x80;
	 			   	mb.txdata[1]=0x3;
	 			   len_pack=2;
	    	    }

	     break;




  	default:
	  	mb.txdata[0]=mb.rxdata[0]+0x80;  // funzione non supportata
	   	mb.txdata[1]=0x1;
	   len_pack=2;
    break;
  	 }
 // prepara la stringa di risposta
   	 w2c (tx,0, mb.Trans_ID);
   	 w2c (tx,2, mb.Protocol_ID);
   	 w2c(tx,4,len_pack+1);
   	 tx[6]=mb.SlaveAddress;
   	 for(i=0;i<len_pack;i++) tx[i+7]=mb.txdata[i];
}

static err_t recv_conn(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err){
      uint16_t  i;
      uint16_t  len;
      char *pc;
      if (err == ERR_OK && p != NULL)
      {
           tcp_recved(pcb, p->tot_len);
           pc=(char *)p->payload; //pointer to the pay load
          len =p->tot_len;//size of the pay load
          for (i=0; i<len; i++) tcp_rx[i]=pc[i]; // copy lwip buffer in tcp_rx
          pbuf_free(p);
          modb_parser(tcp_rx,tcp_tx,len);
          send_mb_answer(pcb,tcp_tx);
      }
   	  else
	  {
   		    sprintf(MSG,"Connessione chiusa  ");
  	        LCD_WriteLine(2,MSG);
            pbuf_free(p);
      }
      return ERR_OK;
}

static err_t conn_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
  LWIP_UNUSED_ARG(err);
  /* Set priority */
  tcp_setprio(pcb, 1);
  tcp_recv(pcb,recv_conn);
  tcp_err(pcb, NULL);
  tcp_poll(pcb, NULL,5);
  sprintf(MSG,"Connesso.  Porta 502");
  LCD_WriteLine(2,MSG);

  return ERR_OK;
}

static int16_t init_conn(ip_addr_t *local_addr,uint16_t port)
{

  err_t err;
  pcb = tcp_new();
  if(pcb == NULL) return(-1);
  tcp_setprio(pcb,1);
  err = tcp_bind(pcb, local_addr,port);
  if(err!=ERR_OK) return(-2);
  tcp_arg(pcb, NULL);
  pcb = tcp_listen(pcb);
  if(pcb == NULL)  return(-3);
  tcp_arg(pcb, pcb);
  tcp_accept(pcb, conn_accept);
  return 0;
}

//==============================================================================
// Global variables

uint16_t value_ip[4];

//==============================================================================
// Global functions

// Required by lwIP library to support any host-related timer functions.
void lwIPHostTimerHandler(void)
{
	    uint32_t IPAddress;
	    static uint32_t ip_attuale;
	    IPAddress = lwIPLocalIPAddrGet();
	    if((IPAddress == 0) || (IPAddress == 0xffffffff))
	    {
	        //
	        // Do nothing and keep waiting.
	        //
	    }
	    else if(IPAddress!=ip_attuale)
	    {
	    	DisplayIPAddress(IPAddress,4);
	    	ip_attuale=IPAddress;
	    }

}

void MyEthernetInit(void)
{
	uint32_t ui32User0, ui32User1;
    uint8_t pui8MACArray[8];
    uint32_t aux;

	// PK4/PK6 are used for Ethernet LEDs.
	ROM_GPIOPinConfigure(GPIO_PK4_EN0LED0);
	ROM_GPIOPinConfigure(GPIO_PK6_EN0LED1);
	GPIOPinTypeEthernetLED(GPIO_PORTK_BASE, GPIO_PIN_4);
	GPIOPinTypeEthernetLED(GPIO_PORTK_BASE, GPIO_PIN_6);

    // Read the MAC address from the user registers.
    ROM_FlashUserGet(&ui32User0, &ui32User1);
    if((ui32User0 == 0xffffffff) || (ui32User1 == 0xffffffff))
    {
    	ui32User0 = 0xAABBCC00;
    	ui32User1 = 0xAABBCCDD;
    }

    // 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);

    ReadEthernetParameters(&parIP);

    if(parIP.IP_address == 0xffffffff)
    {
    	aux=((uint32_t)value_ip[3])+((uint32_t)value_ip[2]<<8)+((uint32_t)value_ip[1]<<16)+((uint32_t)value_ip[0]<<24);
    	parIP.IP_address = aux;
    	//parIP.IP_address = 0xC0A80078; // 192.168.0.120 (per calcolare gli esadecimali devo farli a gruppi di 2)
    	parIP.IP_netmask = 0xFFFFFF00; // 255.255.255.0
    	parIP.IP_gateway = 0x00000000; // 0.0.0.0
    }

    // Initialze the lwIP library, using STATIC IP.
    lwIPInit(g_ui32SysClock, pui8MACArray, parIP.IP_address, parIP.IP_netmask, parIP.IP_gateway, IPADDR_USE_STATIC);

    // Setup the device locator service.
    LocatorInit();
    LocatorMACAddrSet(pui8MACArray);
    LocatorAppTitleSet("MICROCHECK1000XL");
    init_conn(IP_ADDR_ANY,502);
}
