
#include "httpserver.h"
#include "lwip/tcp.h"
#include "fsdata.c"
#include "main.h"
#include "flash_if.h"
#include "common_config.h"
#include "settings_api.h"
#include "crc.h"
#include "gpio_io.h"
#include "systick.h"

#include <string.h>
#include "tinystdio.h"

/**
  * @brief  Общая структура настроек
  */
extern SETTINGS_t sSettings;

extern uint8_t fDoneReset;
extern uint8_t fErrorReset;

char debugMsg[40];

static vu32 DataFlag=0;
static vu32 size =0;
static __IO uint32_t FlashWriteAddress;
static u32 TotalReceived=0;
static char LeftBytesTab[4];
static u8 LeftBytes=0;
static __IO u8 resetpage=0;
static uint32_t ContentLengthOffset =0,BrowserFlag=0;
static __IO uint32_t TotalData=0, checklogin=0;
static bool fEraseFlash = true;

static uint8_t reqCounter = 0;
static uint8_t reqTimer = 0;

struct http_state
{
  char *file;
  u32_t left;
};



htmlpageState htmlpage;
 
static const char http_crnl_2[4] =
/* "\r\n--" */
{0xd, 0xa,0x2d,0x2d};
static const char octet_stream[14] = 
/* "octet-stream" */
{0x6f, 0x63, 0x74, 0x65, 0x74, 0x2d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d,0x0d, };
static const char Content_Length[17] = 
/* Content Length */
{0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c, 0x65, 0x6e, 0x67,0x74, 0x68, 0x3a, 0x20, };


static uint32_t Parse_Content_Length(char *data, uint32_t len);
static void IAP_HTTP_writedata(char* data, uint32_t len);

/* file must be allocated by caller and will be filled in
   by the function. */
static int fs_open(char *name, struct fs_file *file);

uint16_t printLen = 0;
char printBuf[1500];

uint32_t oldAdd = USER_FLASH_FIRST_PAGE_ADDRESS;
uint32_t deltaAdd = 0;

/**
  * @brief  callback function for handling connection errors
  * @param  arg: pointer to an argument to be passed to callback function
  * @param  err: LwIP error code   
  * @retval none
  */
static void conn_err(void *arg, err_t err)
{
  struct http_state *hs;

  hs = arg;
  mem_free(hs);
}

/**
  * @brief  closes tcp connection
  * @param  pcb: pointer to a tcp_pcb struct
  * @param  hs: pointer to a http_state struct
  * @retval
  */
static void close_conn(struct tcp_pcb *pcb, struct http_state *hs)
{
  tcp_arg(pcb, NULL);
  tcp_sent(pcb, NULL);
  tcp_recv(pcb, NULL);
  mem_free(hs);
  tcp_close(pcb);

  reqCounter++;
}

/**
  * @brief sends data found in  member "file" of a http_state struct
  * @param pcb: pointer to a tcp_pcb struct
  * @param hs: pointer to a http_state struct
  * @retval none
  */
static void send_data(struct tcp_pcb *pcb, struct http_state *hs)
{
  err_t err;
  u16_t len;

  /* We cannot send more data than space available in the send
     buffer */
  if (tcp_sndbuf(pcb) < hs->left)
  {
    len = tcp_sndbuf(pcb);
  }
  else
  {
    len = hs->left;
  }
  err = tcp_write(pcb, hs->file, len, 0);
  if (err == ERR_OK)
  {
    hs->file += len;
    hs->left -= len;
  }
}

/**
  * @brief tcp poll callback function
  * @param arg: pointer to an argument to be passed to callback function
  * @param pcb: pointer on tcp_pcb structure
  * @retval err_t
  */
static err_t http_poll(void *arg, struct tcp_pcb *pcb)
{
  if (arg == NULL)
  {
    tcp_close(pcb);
  }
  else
  {
    send_data(pcb, (struct http_state *)arg);
  }
  return ERR_OK;
}

/**
  * @brief callback function called after a successfull TCP data packet transmission  
  * @param arg: pointer to an argument to be passed to callback function
  * @param pcb: pointer on tcp_pcb structure
  * @param len
  * @retval err : LwIP error code
  */
static err_t http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
{
  struct http_state *hs;
  hs = arg;

  if (hs->left > 0)
    send_data(pcb, hs);
  else
  {
    close_conn(pcb, hs);
	
    if (htmlpage == UploadDonePage)
	  fDoneReset = 1;
	else if (htmlpage == UploadErrorPage)
	  fErrorReset = 1;
  }
  return ERR_OK;
}


/**
  * @brief callback function for handling TCP HTTP traffic
  * @param arg: pointer to an argument structure to be passed to callback function
  * @param pcb: pointer to a tcp_pcb structure
  * @param p: pointer to a packet buffer
  * @param err: LwIP error code
  * @retval err
  */
/* goback.cgi - возврат в основную прошивку */
/* upload.cgi - загрузка новой прошивки */
static err_t http_recv(void *arg, struct tcp_pcb *pcb,  struct pbuf *p, err_t err)
{
  int32_t i, len=0;
  uint32_t DataOffset, FilenameOffset;
  char *data, *ptr, filename[13];
  struct fs_file file = {0, 0};
  struct http_state *hs;

  hs = arg;

  
  
  if (err == ERR_OK && p != NULL)
  {
    /* Inform TCP that we have taken the data */
    tcp_recved(pcb, p->tot_len);
    
    if (hs->file == NULL)
    {
      data = p->payload;
      len = p->tot_len;
      
	  printLen = p->tot_len;
	  memcpy(printBuf, p->payload , printLen);
	  //printf(printBuf);
	  
      /* process HTTP GET requests */
      if (strncmp(data, "GET /", 5) == 0)
      {/*
        if ((strncmp(data, "GET /resetmcu.cgi", 17) ==0)&&(htmlpage == UploadDonePage))
        {
          htmlpage = ResetDonePage;
          fs_open("/reset.html", &file);
          hs->file = file.data;
          hs->left = file.len;
          pbuf_free(p);
		  send_data(pcb, hs);   
          resetpage = 1;
          tcp_sent(pcb, http_sent);
        }*/ 
	    if (strncmp(data, "GET /upload.css", 15) == 0)
	    {
	      fs_open("/upload.css", &file);
          hs->file = file.data;
          hs->left = file.len;
          pbuf_free(p);
          send_data(pcb, hs);
          tcp_sent(pcb, http_sent);
	    }
        else if (strncmp(data, "GET /upload.js", 14) == 0)
        {
	      fs_open("/upload.js", &file);
          hs->file = file.data;
          hs->left = file.len;
          pbuf_free(p);
          send_data(pcb, hs);
          tcp_sent(pcb, http_sent);
	    }
	    /* Возврат в основную прошивку. Сбрасываем флаг loadmode,
	       сохраняем настройки и перезагружаемся */
        else if (strncmp(data, "GET /goback.cgi", 15)==0)
        {
        	RTC_WriteBackupRegister(RTC_BKP_DR1, 0);
        /*  sSettings.bootParams.loadMode = 0;
	      SETTINGS_Save();*/
          Delay_ms(1010);
		  NVIC_SystemReset();
        }
        else if (strncmp(data, "GET /favicon.ico", 16) == 0)
        {
          fs_open("/favicon.ico", &file);
          hs->file = file.data;
          hs->left = file.len;
          send_data(pcb, hs);
          tcp_sent(pcb, http_sent);
        }
        else
        {
          /*send the login page (which is the index page) */
          htmlpage = LoginPage;
          fs_open("/index.html", &file);
          hs->file = file.data;
          hs->left = file.len;
          pbuf_free(p);
          send_data(pcb, hs);
          tcp_sent(pcb, http_sent);
        }
      }
#if 1
      /* process POST request for file upload and incoming data packets after POST request*/
      else if ((strncmp(data, "POST /upload.cgi",16) == 0) || (DataFlag >= 1))
      {
		if (fEraseFlash) {
		  FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS);
		  fEraseFlash = false;
		}

        DataOffset = 0;
        
        /* POST Packet received */
        if (DataFlag == 0)
        { 
          BrowserFlag = 0;
          TotalReceived = 0;
          
          /* parse packet for Content-length field */
          size = Parse_Content_Length(data, p->tot_len);
           
          /* parse packet for the octet-stream field */
          for (i = 0; i < len; i++)
          {
             if (strncmp ((char*)(data+i), octet_stream, 13)==0)
             {
               DataOffset = i + 16;
               break;
             }
          }  
          /* case of MSIE8 : we do not receive data in the POST packet*/ 
          if (DataOffset == 0)
          {
             DataFlag++;
             BrowserFlag = 1;
             pbuf_free(p);
             return ERR_OK;
             
          }
          /* case of Mozilla Firefox v3.6 : we receive data in the POST packet*/
          else
          {
            TotalReceived = len - (ContentLengthOffset + 4);
          }
        }
        
        if (((DataFlag ==1)&&(BrowserFlag==1)) || ((DataFlag ==0)&&(BrowserFlag==0)))
        { 
           if ((DataFlag ==0)&&(BrowserFlag==0)) 
           {
             DataFlag++;
           }
           else if ((DataFlag ==1)&&(BrowserFlag==1))
           {
             /* parse packet for the octet-stream field */
             for (i = 0; i < len; i++)
             {
               if (strncmp ((char*)(data+i), octet_stream, 13)==0)
               {
                 DataOffset = i+16;
                 break;
               }
             }
             TotalReceived += len;
             DataFlag++;
           }  
                
           /* parse packet for the filename field */
           FilenameOffset = 0;
           for (i = 0; i < len; i++)
           {
             if (strncmp ((char*)(data+i), "filename=", 9)==0)
             {
                FilenameOffset = i+10;
                break;
             }
           }
           i = 0;
           if (FilenameOffset)
           {
             while((*(data+FilenameOffset + i)!=0x22 )&&(i<13))
             {
               filename[i] = *(data+FilenameOffset + i);
               i++;
             }
             filename[i] = 0x0;
           }
           
           if (i==0)
           {
             htmlpage = FileUploadPage;
             /* no filename, in this case reload upload page */
             fs_open("/upload.html", &file);
             hs->file = file.data;
             hs->left = file.len;
             pbuf_free(p);
             
             /* send index.html page */ 
             send_data(pcb, hs);
          
             /* Tell TCP that we wish be to informed of data that has been
             successfully sent by a call to the http_sent() function. */
             tcp_sent(pcb, http_sent); 
             DataFlag=0;
             return ERR_OK;

           }

           PRINT_USART("\n\r  IAP using HTTP  \n\r");
           sprintf(debugMsg, "File: %s\n\r",filename);
           PRINT_USART(debugMsg);
           PRINT_USART("State: Erasing...\n\r");
          
           TotalData =0 ;
           /* init flash */
           FLASH_If_Init();
           /* erase user flash area */
           //FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS);

           FlashWriteAddress = USER_FLASH_FIRST_PAGE_ADDRESS;
          
           /*indicate start of flash programming */
           PRINT_USART("\n\rState: Programming..\n\r");
         }
         /* DataFlag >1 => the packet is data only  */
         else 
         {
           TotalReceived +=len;
         }
        
         ptr = (char*)(data + DataOffset);
         len-= DataOffset;
        
         /* update Total data received counter */
         TotalData +=len;
        
         /* check if last data packet */
         if (TotalReceived == size)
         {
           /* if last packet need to remove the http boundary tag */
           /* parse packet for "\r\n--" starting from end of data */
           /*
		   i = 4;
           while (strncmp ((char*)(data+ p->tot_len -i),http_crnl_2 , 4))
           {
             i++;
           }
           len -= i;
           TotalData -= i;
           */
           /* write data in Flash */
           if (len)
             IAP_HTTP_writedata(ptr,len);

           DataFlag=0;
           htmlpage = UploadDonePage;

           PRINT_USART("Tot bytes Received:\n\r");
           sprintf(debugMsg, "%d bytes \n\r",TotalData);
           PRINT_USART(debugMsg);
           PRINT_USART("State: Prog Finished \n\r");

		   /* Проверяем CRC */
   		   if (CRC_Read() == CRC_Calcucate()) {
 			 fs_open("/success.html", &file);
             hs->file = file.data;
             hs->left = file.len;
             send_data(pcb, hs);
			 tcp_sent(pcb, http_sent);
			 htmlpage = UploadDonePage;
           }

		   else
		   {
		     fs_open("/error.html", &file);
             hs->file = file.data;
             hs->left = file.len;
             send_data(pcb, hs);
			 tcp_sent(pcb, http_sent);
			 htmlpage = UploadErrorPage;
		   }


           /*
		   sSettings.bootParams.loadMode = 0;
           SETTINGS_Save();
           IO_KeyBlockOn();
           Delay_ms(1010);
	       NVIC_SystemReset();
		   */

        }
        /* not last data packet */
        else
        {
          /* write data in flash */
          if(len)
            IAP_HTTP_writedata(ptr,len);
        }
        pbuf_free(p);
      }
      else
      {
       /* Bad HTTP requests */
        PRINT_USART("Bad HTTP request\n\r");

		pbuf_free(p);
        close_conn(pcb, hs);
      }
#endif
    }
    else
    {
      pbuf_free(p);
      close_conn(pcb,hs);
    }
  }
  if (err == ERR_OK && p == NULL)
  {
    /* received empty frame */
    PRINT_USART("Received empty frame\n\r");

	close_conn(pcb, hs);
  }
  return ERR_OK;
}

/**
  * @brief  callback function on TCP connection setup ( on port 80)
  * @param  arg: pointer to an argument structure to be passed to callback function
  * @param  pcb: pointer to a tcp_pcb structure
  * &param  err: Lwip stack error code
  * @retval err
  */
static err_t http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
  struct http_state *hs;

  /* Allocate memory for the structure that holds the state of the connection */
  hs = mem_malloc(sizeof(struct http_state));

  if (hs == NULL)
  {
    return ERR_MEM;
  }

  /* Initialize the structure. */
  hs->file = NULL;
  hs->left = 0;

  /* Tell TCP that this is the structure we wish to be passed for our
     callbacks. */
  tcp_arg(pcb, hs);

  /* Tell TCP that we wish to be informed of incoming data by a call
     to the http_recv() function. */
  tcp_recv(pcb, http_recv);

  tcp_err(pcb, conn_err);

  tcp_poll(pcb, http_poll, 10);
  return ERR_OK;
}

/**
  * @brief  intialize HTTP webserver  
  * @param  none
  * @retval none
  */
void IAP_httpd_init(void)
{
  struct tcp_pcb *pcb;
  /*create new pcb*/
  pcb = tcp_new();
  /* bind HTTP traffic to pcb */
  tcp_bind(pcb, IP_ADDR_ANY, 80);
  /* start listening on port 80 */
  pcb = tcp_listen(pcb);
  /* define callback function for TCP connection setup */
  tcp_accept(pcb, http_accept);
}

/**
  * @brief  Opens a file defined in fsdata.c ROM filesystem 
  * @param  name : pointer to a file name
  * @param  file : pointer to a fs_file structure  
  * @retval  1 if success, 0 if fail
  */
static int fs_open(char *name, struct fs_file *file)
{
  struct fsdata_file_noconst *f;

  for (f = (struct fsdata_file_noconst *)FS_ROOT; f != NULL; f = (struct fsdata_file_noconst *)f->next)
  {
    if (!strcmp(name, f->name))
    {
      file->data = f->data;
      file->len = f->len;
      return 1;
    }
  }
  return 0;
}

/**
  * @brief  Extract the Content_Length data from HTML data  
  * @param  data : pointer on receive packet buffer 
  * @param  len  : buffer length  
  * @retval size : Content_length in numeric format
  */
static uint32_t Parse_Content_Length(char *data, uint32_t len)
{
  uint32_t i=0,size=0, S=1;
  int32_t j=0;
  char sizestring[6], *ptr;
   
  ContentLengthOffset =0;

  /* find Content-Length data in packet buffer */
  for (i=0;i<len;i++)
  {
    if (strncmp ((char*)(data+i), Content_Length, 16)==0)
    {
      ContentLengthOffset = i+16;
      break;
    }
  }
  /* read Content-Length value */
  if (ContentLengthOffset)
  {
    i=0;
    ptr = (char*)(data + ContentLengthOffset);
    while(*(ptr+i)!=0x0d)
    {
      sizestring[i] = *(ptr+i);
      i++;
      ContentLengthOffset++; 
    }
    if (i>0)
    {
      /* transform string data into numeric format */
      for(j=i-1;j>=0;j--)
      {
        size += (sizestring[j]-0x30)*S;
        S=S*10;
      }
    }
  }
  return size;
}

/**
  * @brief  writes received data in flash    
  * @param  ptr: data pointer
  * @param  len: data length
  * @retval none 
  */
void IAP_HTTP_writedata(char * ptr, uint32_t len)            
{
  uint32_t count, i=0, j=0;
  /* check if any left bytes from previous packet transfer*/
  /* if it is the case do a concat with new data to create a 32-bit word */
  if (LeftBytes)
  {
    while(LeftBytes<=3)
    {
      if(len>(j+1))
      {
        LeftBytesTab[LeftBytes++] = *(ptr+j);
      }
      else
      {
        LeftBytesTab[LeftBytes++] = 0xFF;
      }
      j++;
    }
    FLASH_If_Write(&FlashWriteAddress, (u32*)(LeftBytesTab),1);
	
    LeftBytes =0;
    
    /* update data pointer */
    ptr = (char*)(ptr+j);
    len = len -j;
  }
  
  /* write received bytes into flash */
  count = len/4;
  
  /* check if remaining bytes < 4 */
  i= len%4;
  if (i>0)
  {
    if (TotalReceived != size)
    {
      /* store bytes in LeftBytesTab */
      LeftBytes=0;
      for(;i>0;i--)
      LeftBytesTab[LeftBytes++] = *(char*)(ptr+ len-i);  
    }
    else count++;
  }
  FLASH_If_Write(&FlashWriteAddress, (u32*)ptr ,count);
}

uint32_t ReturnFlashWriteAddress(void)
{
  return FlashWriteAddress;
}


/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
