/* 
/******************************************************************************
*
*       XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
*       AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
*       SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
*       OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
*       APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
*       THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
*       AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
*       FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
*       WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
*       IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
*       REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
*       INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*       FOR A PARTICULAR PURPOSE.
*
*       (c) Copyright 2002 Xilinx Inc.
*       All rights reserved.
*
******************************************************************************/
/*****************************************************************************/


/***************************** Include Files *********************************/
#include <stdio.h>
#include <string.h>
#include <net/xilnet_config.h>
#include <net/xilsock.h>
#include <mfs_config.h>
#include <web.h>
#include <xgpio_l.h>
#include <xemac_l.h>
#include <xtft.h>
#include <xtft_l.h>
#include <xparameters.h>
#include "xuartns550_l.h"
#include "bootload_graphics.h"
#include "xromlcd.h"

#define UART_BASEADDR XPAR_OPB_UART16550_0_BASEADDR
#define UART_CLOCK    XPAR_XUARTNS550_CLOCK_HZ
#if !SIM
#define UART_BAUDRATE 9600                      /* real hardware */
#else
#define UART_BAUDRATE (UART_CLOCK / 16 / 3)     /* simulation */
#endif

#define XPAR_MYETHER_BASEADDR XPAR_OPB_ETHERNET_0_BASEADDR
#define XPAR_MYGPIO_BASEADDR XPAR_OPB_GPIO_0_BASEADDR
#define TFT_DEVICE_ID   XPAR_PLB_TFT_CNTLR_REF_0_DEVICE_ID


/*
 * Function prototypes and global variables
 */

#define BUFSIZE 50
char buf[BUFSIZE];
unsigned char dip[8];
unsigned char *bg_tab[] = {"#63B1B1","#FFFFCC","#808040","#FFCC99","#CC6600", "#99FF99"};
unsigned char http_hdr[17] = {0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d,0x0a};
unsigned char http_hdr_2[27] = {"Content-type: text/html\n\r\n\r"};  
unsigned char file_not_found[24] = {0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x34,0x30,0x34,0x20,0x4e,
                                    0x6f,0x74,0x20,0x46,0x6f,0x75,0x6e,0x64,0x0d,0x0a};  
unsigned int led_tab[16] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf};
unsigned char d2 = 0xf;
int count = 0;

void do_proc_req(int);
void create_index (void);
void do_proc_conns();

int main()
{
   int n = 0;
   int s;
   int err;
   struct sockaddr_in addr;
   unsigned int gpio_data = 0;
   XTft Tft;
   int i;

   volatile unsigned int macval1 = *((unsigned int*)(0x80f0110c));
   volatile unsigned int macval2 = *((unsigned int*)(0x80f01110));

   char *text[] = {"Welcome to the ML401 Webserver Demo",
                   "",
                   "This demonstration is an ethernet controlled GPIO interface. A remote",
                   "host running a web browser can read the value of the ML401 DIP switches or",
                   "set the LEDs on the board. Refreshing the remote PC's browser causes the",
                   "background color to change and the current DIP switch values to be read.",
                   "By default, the IP address of the board is 1.2.3.4 but can be changed by",
                   "recompiling the software.",
                   "",
                   "Instructions for running this demo:",
                   "1. Connect an ethernet crossover cable from your host to this board.",
                   "2. Configure the remote host's IP address to 1.2.3.9",
                   "3. Make sure the connection is running at 10 or 100 mbps and link lights are on",
                   "4. On the remote host, open a web browser connection to http://1.2.3.4:8080",
                   "   Follow the instructions on the loaded web page",
                   "5. On the remote host, you can also ping 1.2.3.4 to see the connection",
                   "",
                   "Now Starting Webserver Demo...",
                   "(Note: status messages are output to the UART)",
                   "",
                   ""};

   XUartNs550_SetBaud(UART_BASEADDR, UART_CLOCK, UART_BAUDRATE);
   XUartNs550_mSetLineControlReg(UART_BASEADDR, XUN_LCR_8_DATA_BITS);
   /* Initialize LCD and display */
   XromLCDOn();
   XromLCDInit();
   XromLCDPrintString ("ML401 = http:// ", "1.2.3.4:8080    ");

   /* Initialize TFT and display */
   XTft_Initialize(&Tft, TFT_DEVICE_ID);
   XTft_SetColor(&Tft, 0x00ffffff, 0x00404040);
   XTft_ClearScreen(&Tft);
   for (i = 0; i < 20; i++)
     {
     XTft_SetPos(&Tft, 5, (i*15)+5);
     XTft_WriteColorString(&Tft,text[i], 0x0000FF80, 0x00404040);
     xil_printf (text[i]);
     xil_printf ("\r\n");
     }

   /*
    * Set up MAC & IP addresses and intialise Ethernet HW Table
    */
   xilnet_eth_init_hw_addr("00:00:00:00:22:38");

   xilnet_eth_init_hw_addr_tbl();
   xilnet_ip_init("1.2.3.4");
   
   /*
    * Initialize MAC Baseaddress, set MAC address and enable it
    */
   xilnet_mac_init(XPAR_MYETHER_BASEADDR);
   XEmac_mSetMacAddress(XPAR_MYETHER_BASEADDR, mb_hw_addr);

   //   print("MAC1 : "); putnum(macval1);
   //   print("MAC2 : "); putnum(macval2);

   XEmac_mEnable(XPAR_MYETHER_BASEADDR);

   xil_printf("ip: %d.%d.%d.%d \r\n\r\n", mb_ip_addr[0], mb_ip_addr[1],mb_ip_addr[2],mb_ip_addr[3]);
   
   /*
    * Initialise the File System and create files
    */
   mfs_init_fs(MFS_NUMBYTES, (char*) MFS_BASE_ADDRESS, MFS_INIT_TYPE);
   //mfs_init_fs(25*sizeof(struct mfs_file_block), (char*) mfs_file_system, MFS_INIT_TYPE);

   create_index();
   /*
    * set LEDs to output and DIP switches to input
    */
   XGpio_mSetDataDirection(XPAR_MYGPIO_BASEADDR, 1, 0xFFFFFFF0);
   
   /*
    * Create, Bind, Listen on Server Socket
    */
   s = xilsock_socket(AF_INET, SOCK_STREAM, 0);
   if (s == -1) {
      xil_printf("socket() error \r\n");
      exit(1);
   }
   addr.sin_family = AF_INET;
   addr.sin_addr.s_addr = INADDR_ANY;
   addr.sin_port = SERVER_PORT;
   err = xilsock_bind(s, (struct sockaddr *)&addr, sizeof(struct sockaddr));
   if (err == -1) {
      xil_printf("bind() error \r\n");
      exit(1);
   }
   err = xilsock_listen(s, 4);
   if (err == -1) {
      xil_printf("listen() error \r\n");
      exit(1);
   }

   /*
    * Start Accepting Connections on Server Socket
    */
   for (;;) {
      
      int alen = 0; 

      /*
       * Set gpio_data to be displayed on LEDs
       */
      count = count % 6;        
      gpio_data = ((unsigned short)d2);
      XGpio_mSetDataReg(XPAR_MYGPIO_BASEADDR, 1, gpio_data);

      /*
       * Accept new connections
       */
      alen = sizeof(struct sockaddr);
      err = xilsock_accept(s, (struct sockaddr *)&addr, &alen);
      if (xilsock_status_flag & XILSOCK_NEW_CONN) {
         /*
          * Add new web connection
          */
         add_web_conn(err);

      } 
      /*
       * Process all existing connections
       */
      do_proc_conns();

   }
   return(0);
}


/*
 * Web Connections Management
 */
unsigned char iswebinit = 0;

/*
 * Initialise all web conns
 */
void init_web_conns() {
   int i;

   for (i = 0; i < MAX_WEB_CONNS; i++) {
      web_conns[i].s =-1;
      web_conns[i].file_name = NULL;
      web_conns[i].bytes_rem = 0;
      web_conns[i].state = WEB_CONN_FREE;
   }
   iswebinit = 1;
}


/*
 * Add a web connection
 */
int add_web_conn(int s) {
   int i;

   if (!iswebinit)
      init_web_conns();
   
   for ( i = 0; i < MAX_WEB_CONNS; i++) {
      if (web_conns[i].state == WEB_CONN_FREE)
         break;
   }
   if ( i <= MAX_WEB_CONNS) {
      web_conns[i].s = s;
      web_conns[i].state = WEB_GET;
      return i;
   }
   xil_printf("no free conns \r\n");
   return -1;
}


/*
 * Free a web connection
 */
void free_web_conn(int s) {
   web_conns[s].s = -1;
   web_conns[s].state = WEB_CONN_FREE;
}

/*
 * Process all web connections
 */
void do_proc_conns() {
   int i;
   
   for (i = 0; i < MAX_WEB_CONNS; i++){
      do_proc_req(i);
      // reset buf ptrs of sockets
      xilsock_sockets[web_conns[i].s].recvbuf.buf = NULL;
      xilsock_sockets[web_conns[i].s].recvbuf.size = 0;
   }
}



/* 
 * Routine that processes requests : 
 * filenames < 10 ; 
 */


#define SND_PTR_SIZE (1500-40)
unsigned char *index1 = "<html><head></head><body bgcolor=\"";
unsigned char *bg = "#63B1B1";

unsigned char *index_html = "\"><center><b><font size=+3>MicroBlaze Web Server on ML401 Virtex 4 Board</font></b></center><br><font face=\"Garamond\">A Simple Web Server running on <b>MicroBlaze</b>.Check out the following demos.</font><p><b><font face=\"Garamond\">4-bit LED Display:</font></b><p><font face=\"Garamond\">Type in a hex value and see it displayed as a 4-bit binary value on the LED display on reload.</font><br><form action=\"pl\" method=\"get\"><b><font face=\"Garamond\">Hex Value</font></b><input type=\"text\" size=\"1\" name=\"textbox\"><p><input type=\"submit\" value=\"Submit\"><br></form><font face=\"Garamond\"></font><p><b><font face=\"Garamond\">Changing Colors :</font></b><p><font face=\"Garamond\">See the background color of the page change everytime when reloaded.<p><a href=\"index2.html\">Yet Another File on MicroBlaze</a></font><p><b><font face=\"Garamond\">DIP Switches:</font></b><font face=\"Garamond\"></font><p><font face=\"Garamond\">Set Dip Switches 1 ... 8 and see it displayed as binary on reload</font><b><font face=\"Garamond\"></font></b><p><b><font face=\"Garamond\">DIP Value: ";

unsigned char* index_end = "</font></b></body></html>";


unsigned char *index2_html = "\"><center><b><font size=+3>MicroBlaze Web Server on ML401 Virtex 4 Board</font></b></center><br><font face=\"Garamond\">index2.html on <b>MicroBlaze</b>.<p><a href=\"index.html\">Back</a></font></body></html>";


/* Constants for GET */
#define G 0x47
#define E 0x45
#define T 0x54

/*
 * Process a Web Connection
 */

void do_proc_req(int n) {
   char    fname[15] = {0};
   int     j, num, fd;
   char *tok;
   unsigned int dip_val = 0;
   unsigned char *sndptr = (unsigned char*)(sendbuf+LINK_HDR_LEN+IP_HDR_LEN*4+(TCP_HDR_LEN*4));
   int i1len, bglen;
   unsigned char * buf = xilsock_sockets[web_conns[n].s].recvbuf.buf;

   if (!buf)
      return;
   
   /*
    * Ack for Data Sent
    */
   if (xilsock_status_flag & XILSOCK_TCP_ACK) {
      xilsock_close(web_conns[n].s);
      free_web_conn(n); 
   }
   /*
    * GET Request
    */
   else if (xilsock_status_flag & XILSOCK_TCP_DATA) {
      /*
       * Read DIP switches
       */
      dip_val = XGpio_mGetDataReg(XPAR_MYGPIO_BASEADDR, 1);
      dip[7] = ((dip_val & 0x00004000)>>14) + 48;
      dip[6] = ((dip_val & 0x00008000)>>15) + 48;
      dip[5] = ((dip_val & 0x00010000)>>16) + 48;
      dip[4] = ((dip_val & 0x00020000)>>17) + 48;
      dip[3] = ((dip_val & 0x00040000)>>18) + 48;
      dip[2] = ((dip_val & 0x00080000)>>19) + 48;
      dip[1] = ((dip_val & 0x00100000)>>20) + 48;
      dip[0] = ((dip_val & 0x00200000)>>21) + 48;
      
      i1len = strlen(index1);
      bglen = strlen(bg_tab[count]);
      tok = strtok(buf, " ");
      tok = strtok('\0', " ");
      tok = tok+1;
      
      xil_printf("Processing Request...\r\n");
      if (tok[0] == 'p' && tok[1] == 'l') {
         d2 = tok[11];
         
         switch(d2) {
         case '0': 
            d2 = led_tab[0];
            break;
         case '1':
            d2 = led_tab[1];
            break;
         case '2':
            d2 = led_tab[2];
            break;
         case '3':
            d2 = led_tab[3];
            break;
         case '4':
            d2 = led_tab[4];
            break;
         case '5':
            d2 = led_tab[5];
            break;
         case '6':
            d2 = led_tab[6];
            break;
         case '7':
            d2 = led_tab[7];
            break;
         case '8':
            d2 = led_tab[8];
            break;
         case '9':
            d2 = led_tab[9];
            break;
         case 'a': case 'A':
            d2 = led_tab[10];
            break;
         case 'b': case 'B':
            d2 = led_tab[11];
            break;
         case 'c': case 'C': 
            d2 = led_tab[12];
            break;
         case 'd': case 'D':
            d2 = led_tab[13];
            break;
         case 'e': case 'E':
            d2 = led_tab[14];
            break;
         case 'f': case 'F':
            d2 = led_tab[15];
            break;
         default:
            d2 = led_tab[0];
            break;
         }      
      }
      else {
         strcpy(fname,tok);
      }
      memset(sendbuf, 0, LINK_FRAME_LEN);
      
      /*
       * Fill HTTP header and send requested file
       */
      memcpy(sndptr, http_hdr, 17);
	  memcpy(sndptr+17, http_hdr_2, 27);
      if (!(*fname))
         strcpy(fname, "index.html");
      if ( (fd = mfs_file_open(fname, MFS_MODE_READ)) < 0) {
         xil_printf("file not found \r\n");       
         memcpy(sndptr, file_not_found, 24);
         memcpy(&sndptr[24], "File Not Found", strlen("File Not Found"));
         n = xilsock_send(web_conns[n].s, sendbuf, strlen(sndptr));
      }
      else {
         int diplen = 0;
         j = mfs_file_read(fd, (sndptr+17+27+i1len+bglen), (1514-14-20-20));
         if (!strcmp(fname, "index.html")) {
            strcat((sndptr+17+27+i1len+bglen), dip);
            diplen = 8;
         }
         memcpy((sndptr+17+27+i1len), bg_tab[count], bglen);
         memcpy((sndptr+17+27), index1, i1len);
         num = xilsock_send(web_conns[n].s, sendbuf, j+17+27+i1len+bglen+diplen);
         memset(sendbuf, 0, 1514);
         xil_printf("done..\r\n");
         mfs_file_close(fd);
      }
      count++;      
   } 
}

/*
 * Create Index and Index1 HTML files
 */
void create_index(void) {
   int fd;
   int tmp;
   
   if ( (fd = mfs_file_open("index.html", MFS_MODE_CREATE)) == -1) {
      xil_printf("cant create\r\n");
      exit(-1);
   }
   if ( (fd = mfs_file_open("index.html", MFS_MODE_WRITE)) == -1) { 
      xil_printf("cant open index for write\r\n"); 
      exit(-1); 
   } 
   if ( (tmp = mfs_file_write(fd, index_html, strlen(index_html))) == 0) {
      xil_printf("cant write to file \r\n");
      exit(-1);
   }
   mfs_file_close(fd);



   if ( (fd = mfs_file_open("index2.html", MFS_MODE_CREATE)) == -1) {   
      xil_printf("index2 cant create\r\n");   
      exit(-1);   
   }   
   if ( (fd = mfs_file_open("index2.html", MFS_MODE_WRITE)) == -1) {   
      xil_printf("index2 cant open for write\r\n");   
      exit(-1);   
   }   
   if ( (tmp = mfs_file_write(fd, index2_html, strlen(index2_html))) == 0) {
      xil_printf("cant write index2\r\n");
      exit(-1);
   }
   mfs_file_close(fd);   


}

/*
 * Stack Overflow & Interrupt Handler Dummy Functions
 */
void _stack_overflow_exit(void) {
   xil_printf("Stack Overflow \r\n");
}

void _interrupt_handler() {
}
