// ******************************************************************************
//
//
//  Version/Date    Comment
//   2026.05.28     - First release
// ******************************************************************************

#include <stdbool.h>
#include <ustdlib.h>

#include "FreeRTOS.h"
#include "task.h"

#ifdef FREERTOS_PLUS_TCP
  #include "FreeRTOS_IP.h"
  #include "FreeRTOS_Sockets.h"
#endif

#ifdef FREERTOS_PLUS_LWIP
  #include "lwip/opt.h"
  #include "lwip/arch.h"
  #include "lwip/init.h"
  #include "lwip/sockets.h"
#endif

#ifdef FREERTOS_PLUS_OPCUA
  #include "open62541/config.h"
#endif

// For resetting the device
#include "rom.h"
#include "rom_map.h"

#include "TaskWebServer.h"

#ifdef FREERTOS_PLUS_TCP
  #ifndef configWEBSERVER_TX_WINDOW_SIZE
    #define configWEBSERVER_TX_WINDOW_SIZE    2
  #endif
  #ifndef configWEBSERVER_RX_WINDOW_SIZE
    #define configWEBSERVER_RX_WINDOW_SIZE    2
  #endif
#endif

// The maximum time to wait for a closing socket to close
#define TASKWEBSERVER_SHUTDOWN_DELAY    ( pdMS_TO_TICKS( 5000 ) )

// The standard http port number
#define TASKWEBSERVER_PORT_NUMBER     80

// ------------------------------------------------------------------------------

// Forwards
static void TaskWebServer_ConnectionListeningTask( void * pvParameters );
static void TaskWebServer_ConnectionInstanceTask ( void * pvParameters );

uint32_t TaskWebServer_ProcessDataReceived       ( uint8_t *ARxBuffer, uint32_t ARxBytes, uint8_t *ATxBuffer );
void     TaskWebServer_SystemReset               ( void );

// ------------------------------------------------------------------------------

// Stores the stack size
static uint16_t TaskWebServer_UsedStackSize = 0;

static uint16_t TaskWebServer_SomeCounter = 0;
static bool     TaskWebServer_ResetDevice = false;


// ------------------------------------------------------------------------------
//  Descript: Start the web server (listener) task
//  Params  : <AStackSize>  Stack size
//  Params  : <APriority>   Priority
//  Returns : -
// ------------------------------------------------------------------------------
void TaskWebServer_StartWebServerTasks( size_t AStackSize, UBaseType_t APriority )
{
  configPRINTF( ( "WebServer task init\n" ) );

  if (xTaskCreate(
      TaskWebServer_ConnectionListeningTask,  // The function that implements the task
      "WebServerListener",                    // Just a text name for the task to aid debugging
      AStackSize,                             // The stack size
      NULL,                                   // The task parameter, not used in this case
      APriority,                              // The priority assigned to the task
      NULL ) != pdPASS)
  {
    configPRINTF( ( "WebServer task init task failure\n" ) );
    while (1) {}
  }
  // Remember the requested stack size so it can be re-used by the server listening task when it creates tasks to handle connections
  TaskWebServer_UsedStackSize = AStackSize;
}


// ------------------------------------------------------------------------------
//  Descript: Listener task (starts instance if a client connects)
//  Params  : <AParameters>  Parameters
//  Returns : -
// ------------------------------------------------------------------------------
static void TaskWebServer_ConnectionListeningTask( void * AParameters )
{
#ifdef FREERTOS_PLUS_TCP
  struct freertos_sockaddr LClient         , LBindAddress;
         Socket_t          LListeningSocket, LConnectedSocket;
#endif
#ifdef FREERTOS_PLUS_LWIP
  struct sockaddr_in LClient         , LBindAddress;
         int         LListeningSocket, LConnectedSocket;
  struct timeval     LTimeout;
#endif
  socklen_t LSize = sizeof( LClient );
  static const TickType_t LReceiveTimeOut = portMAX_DELAY;
  const BaseType_t LBacklog = 20;

  configPRINTF( ( "WebServer listener task started\n" ) );

#ifdef FREERTOS_PLUS_TCP
  #if ( ipconfigUSE_TCP_WIN == 1 )
    WinProperties_t LWinProps;

    // Fill in the buffer and window sizes that will be used by the socket
    LWinProps.lTxBufSize = ipconfigTCP_TX_BUFFER_LENGTH;
    LWinProps.lTxWinSize = configWEBSERVER_TX_WINDOW_SIZE;
    LWinProps.lRxBufSize = ipconfigTCP_RX_BUFFER_LENGTH;
    LWinProps.lRxWinSize = configWEBSERVER_RX_WINDOW_SIZE;
  #endif
#endif

  // Just to prevent compiler warnings
  ( void ) AParameters;

  // Attempt to open the socket
#ifdef FREERTOS_PLUS_TCP
  LListeningSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );
  configASSERT( LListeningSocket != FREERTOS_INVALID_SOCKET );
  // Set a time out so accept() will just wait for a connection
  FreeRTOS_setsockopt( LListeningSocket, 0, FREERTOS_SO_RCVTIMEO, &LReceiveTimeOut, sizeof( LReceiveTimeOut ) );
  // Set the window and buffer sizes
  #if ( ipconfigUSE_TCP_WIN == 1 )
    FreeRTOS_setsockopt( LListeningSocket, 0, FREERTOS_SO_WIN_PROPERTIES, ( void * ) &LWinProps, sizeof( LWinProps ) );
  #endif
#endif

#ifdef FREERTOS_PLUS_LWIP
  LListeningSocket = lwip_socket(AF_INET, SOCK_STREAM, 0);
  // Set a time out so accept() will just wait for a connection
  LTimeout.tv_sec = LReceiveTimeOut;
  LTimeout.tv_usec = 0;
  lwip_setsockopt( LListeningSocket, SOL_SOCKET, SO_RCVTIMEO, &LTimeout, sizeof( LTimeout ) );
#endif

  // Bind the socket to the port that the client task will send to, then listen for incoming connections
  LBindAddress.sin_port = TASKWEBSERVER_PORT_NUMBER;
#ifdef FREERTOS_PLUS_TCP
  LBindAddress.sin_port = FreeRTOS_htons( LBindAddress.sin_port );
  LBindAddress.sin_family = FREERTOS_AF_INET;
  FreeRTOS_bind  ( LListeningSocket, &LBindAddress, sizeof( LBindAddress ) );
  FreeRTOS_listen( LListeningSocket, LBacklog );
#endif
#ifdef FREERTOS_PLUS_LWIP
  LBindAddress.sin_addr.s_addr = INADDR_ANY;
  LBindAddress.sin_port = htons( LBindAddress.sin_port );
  LBindAddress.sin_family = AF_INET;
  lwip_bind  (LListeningSocket, (struct sockaddr *)&LBindAddress, sizeof(LBindAddress));
  lwip_listen(LListeningSocket, LBacklog);
#endif

  for( ; ; )
  {
    // Wait for a client to connect
#ifdef FREERTOS_PLUS_TCP
    LConnectedSocket = FreeRTOS_accept( LListeningSocket, &LClient, &LSize );
    configASSERT( LConnectedSocket != FREERTOS_INVALID_SOCKET );
#endif
#ifdef FREERTOS_PLUS_LWIP
    LConnectedSocket = lwip_accept( LListeningSocket, (struct sockaddr*)&LClient, &LSize );
    configASSERT( LConnectedSocket >= 0 );
#endif

    configPRINTF( ( "WebServer listener accept from client\n" ) );
    if (xTaskCreate(
          TaskWebServer_ConnectionInstanceTask, // The function that implements the task
          "WebServer",                          // Just a text name for the task to aid debugging
          TaskWebServer_UsedStackSize,          // The stack size
          (void *) LConnectedSocket,            // The task parameter
          tskIDLE_PRIORITY,                     // The priority assigned to the task
          NULL ) != pdPASS)
    {
      configPRINTF( ( "WebServer listener task failure\n" ) );
      while (1) {}
    }
  }
}


// ------------------------------------------------------------------------------
//  Descript: Connection instance task
//  Params  : <AParameters>  Parameters
//  Returns : -
// ------------------------------------------------------------------------------
static void TaskWebServer_ConnectionInstanceTask( void * AParameters )
{
               int32_t     lBytes, lSent, lToSend;
#ifdef FREERTOS_PLUS_TCP
               Socket_t    LConnectedSocket;
#endif
#ifdef FREERTOS_PLUS_LWIP
               int         LConnectedSocket;
  struct       timeval     xTimeout;
#endif
  static const TickType_t  xReceiveTimeOut = pdMS_TO_TICKS( 5000 );
  static const TickType_t  xSendTimeOut    = pdMS_TO_TICKS( 5000 );
               TickType_t  xTimeOnShutdown;
               uint8_t    *pucRxBuffer;
               uint8_t    *pucTxBuffer    = NULL;
               uint8_t    *pucTxBufferOrg = NULL;

  configPRINTF( ( "WebServer listener instance task started\n" ) );

#ifdef FREERTOS_PLUS_TCP
  LConnectedSocket = ( Socket_t ) AParameters;
#endif
#ifdef FREERTOS_PLUS_LWIP
  LConnectedSocket = ( int ) AParameters;
#endif

  // Attempt to create the buffer used to receive and send data
#ifdef FREERTOS_PLUS_TCP
  pucRxBuffer = ( uint8_t * ) pvPortMalloc( ipconfigTCP_MSS );
#endif
#ifdef FREERTOS_PLUS_LWIP
  pucRxBuffer = ( uint8_t * ) pvPortMalloc( TCP_MSS );
#endif
  if( pucRxBuffer != NULL )
  {
#ifdef FREERTOS_PLUS_TCP
    FreeRTOS_setsockopt( LConnectedSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) );
    FreeRTOS_setsockopt( LConnectedSocket, 0, FREERTOS_SO_SNDTIMEO, &xSendTimeOut   , sizeof( xReceiveTimeOut ) );
#endif
#ifdef FREERTOS_PLUS_LWIP
    xTimeout.tv_sec = xReceiveTimeOut;
    xTimeout.tv_usec = 0;
    lwip_setsockopt( LConnectedSocket, SOL_SOCKET, SO_RCVTIMEO, &xTimeout, sizeof( xTimeout ) );
    xTimeout.tv_sec = xSendTimeOut;
    xTimeout.tv_usec = 0;
    lwip_setsockopt( LConnectedSocket, SOL_SOCKET, SO_SNDTIMEO, &xTimeout, sizeof( xTimeout ) );
#endif

    // Our sending buffer containes -all- data before it is being transmitted in chunks, so it is considerably larger
    pucTxBufferOrg = ( uint8_t * ) pvPortMalloc( 10000 );
    // Since we will be manipulating the buffer pointre, use a copy (otherwise releasing the meory will be an issue)
    pucTxBuffer = pucTxBufferOrg;

    for( ; ; )
    {
      // Zero out the receive array so there is NULL at the end of the string
#ifdef FREERTOS_PLUS_TCP
      memset( pucRxBuffer, 0x00, ipconfigTCP_MSS );
#endif
#ifdef FREERTOS_PLUS_LWIP
      memset( pucRxBuffer, 0x00, TCP_MSS );
#endif

      // Receive data on the socket
#ifdef FREERTOS_PLUS_TCP
      lBytes = FreeRTOS_recv( LConnectedSocket, pucRxBuffer, ipconfigTCP_MSS, 0 );
#endif
#ifdef FREERTOS_PLUS_LWIP
      lBytes = lwip_recv( LConnectedSocket, pucRxBuffer, TCP_MSS, 0 );
#endif

      // If data was received, process it
      if( lBytes >= 0 )
      {
        lSent = 0;

        // Create the data to be sent in pucTxBuffer
        lToSend = TaskWebServer_ProcessDataReceived( pucRxBuffer, lBytes, pucTxBuffer );

        // Call send() until all the data has been sent or an error is indicated in lSent
        while( ( (lSent >= 0) && (lToSend > 0 ) ) )
        {
#ifdef FREERTOS_PLUS_TCP
          lSent = FreeRTOS_send( LConnectedSocket, pucTxBuffer, lToSend, 0 );
#endif
#ifdef FREERTOS_PLUS_LWIP
          lSent = lwip_send( LConnectedSocket, pucTxBuffer, lToSend, 0 );
#endif
          // Adjust count / pointer to data
          lToSend     -= lSent;
          pucTxBuffer += lSent;
        }
        // Done (each requests also closes the connection)
        break;
      }
      else
      {
        // Socket closed?
        break;
      }
    }
    vPortFree( pucTxBufferOrg );
  }

  // Initiate a shutdown in case it has not already been initiated
#ifdef FREERTOS_PLUS_TCP
  FreeRTOS_shutdown( LConnectedSocket, FREERTOS_SHUT_RDWR );
#endif
#ifdef FREERTOS_PLUS_LWIP
  lwip_shutdown( LConnectedSocket, SHUT_RDWR );
#endif

  // Wait for the shutdown to take effect, indicated by FreeRTOS_recv() returning an error
  xTimeOnShutdown = xTaskGetTickCount();
  do
  {
#ifdef FREERTOS_PLUS_TCP
    if( FreeRTOS_recv( LConnectedSocket, pucRxBuffer, ipconfigTCP_MSS, 0 ) < 0 )
      break;
#endif
#ifdef FREERTOS_PLUS_LWIP
    if( lwip_recv( LConnectedSocket, pucRxBuffer, TCP_MSS, 0 ) < 0 )
      break;
#endif
  } while( ( xTaskGetTickCount() - xTimeOnShutdown ) < TASKWEBSERVER_SHUTDOWN_DELAY );

  // Finished with the socket, buffer, the task
  vPortFree( pucRxBuffer );
#ifdef FREERTOS_PLUS_TCP
  FreeRTOS_closesocket( LConnectedSocket );
#endif
#ifdef FREERTOS_PLUS_LWIP
  lwip_close( LConnectedSocket );
#endif

  configPRINTF( ( "WebServer listener instance task stopped\n" ) );

  if (TaskWebServer_ResetDevice)
    TaskWebServer_SystemReset();

  vTaskDelete( NULL );
}


// ------------------------------------------------------------------------------
//  Descript: Helper function to write to buffer only the number of bytes that are available
//  Params  : <ABuffer>      Pointer to the output buffer
//            <ABufferSize>  Buffer size
//            <AFormat>      Formatting
//  Returns : <return>       Length of added data
// ------------------------------------------------------------------------------
uint32_t SystemWriteToBuffer(uint8_t *ABuffer, uint32_t ABufferSize, const char *AFormat, ...)
{
  if (ABufferSize > 0)
  {
    va_list LArg;
    int     LWritten;
    va_start(LArg, AFormat);
    LWritten = uvsnprintf((char *)ABuffer, ABufferSize, AFormat, LArg);
    va_end(LArg);
    if (LWritten < ABufferSize)
      return LWritten;
    else
      return ABufferSize;
  }
  return 0;
}


// ------------------------------------------------------------------------------
//  Descript: Reset device
//  Params  : -
//  Returns : -
// ------------------------------------------------------------------------------
void TaskWebServer_SystemReset( void )
{
  uint32_t LLoop;
  // Reset the device
  configPRINTF( ( "Resetting the device\n" ) );
  // Small delay (allow print to complete)
  for (LLoop=0; LLoop<100000; LLoop++) ;
  MAP_SysCtlReset();
}


// ------------------------------------------------------------------------------
//  Descript: Data received processing
//  Params  : <ARxBuffer>  Pointer to received data buffer
//            <ARxBytes>   Size of received data
//            <ATxBuffer>  Pointer to transmit buffer
//  Returns : <return>     Size of data in transmit buffer
// ------------------------------------------------------------------------------
uint32_t TaskWebServer_ProcessDataReceived(uint8_t *ARxBuffer, uint32_t ARxBytes, uint8_t *ATxBuffer)
{
  uint8_t   *LBufferIn;
  uint8_t   *LBufferOut;
  uint32_t   LLength;
  uint32_t   LSize;
  uint32_t   LMaxSize;
  uint32_t   LState;
  uint8_t    LRequestPath;
  char       LGetParameter[16];

  LBufferIn  = ARxBuffer;
  LBufferOut = ATxBuffer;
  LLength    = ARxBytes;
  LSize      = 0;
  LState     = 2;
  LMaxSize   = 10000;

  LRequestPath = '?';

  // If "GET" command, finish processing or return
  //   GET / HTTP..... (default request)
  //   GET /d     Show debug page
  //   GET /e     Show exception table
  //   GET /ec    Clear exceptions, show home page
  //   GET /j     See /j?
  //   GET /j?    Set data (GET /j generates this)
  //   GET /l0    Generate watchdog error
  //   GET /m0    Generate exception
  //   GET /px=v  Set port to value
  //   GET /r     Reset counters
  //   GET /z     Restart system
  if (LLength >= 6 && *LBufferIn++ == 'G' && *LBufferIn++ == 'E' && *LBufferIn++ == 'T'  && *LBufferIn++ == ' '  && *LBufferIn++ == '/')
  {
    LLength -= 5;
    // Get all until next space
    LSize = 0;
    if (LLength > 0)
      while ((*LBufferIn != ' ') && (LSize < (sizeof(LGetParameter)-2)) )
        LGetParameter[LSize++] = *LBufferIn++;
    // End with zero (for string operations)
    LGetParameter[LSize++] = 0;
    LRequestPath = '?';

    configPRINTF( ( "Request: %s\n", LGetParameter ) );

    // If "GET /" command
    if (strcmp(LGetParameter, "") == 0)
    {
      LRequestPath = ' ';
      configPRINTF( ( "Webpage empty / detected\n" ) );
    }

    // If "GET /d" command
    if (strcmp(LGetParameter, "d") == 0)
    {
      LRequestPath = 'd';
      configPRINTF( ( "Webpage /d detected\n" ) );
    }

    // If "GET /e" command
    if (strcmp(LGetParameter, "e") == 0)
    {
      LRequestPath = 'e';
      configPRINTF( ( "Webpage /e detected\n" ) );
    }

    if (strcmp(LGetParameter, "ec") == 0)
    {
      LRequestPath = 'e' + 'c';
      configPRINTF( ( "Webpage /ec detected\n" ) );
    }

    // If "GET /j" command, display JTAG page
    if (strcmp(LGetParameter, "j") == 0)
    {
      LRequestPath = 'j';
      configPRINTF( ( "Webpage /j detected\n" ) );
    }

    // If "GET /l0" command
    if (strcmp(LGetParameter, "l0") == 0)
    {
      LRequestPath = 'l';
      configPRINTF( ( "Webpage /l0 detected\n" ) );
    }

    // If "GET /m0" command
    if (strcmp(LGetParameter, "m0") == 0)
    {
      LRequestPath = 'm';
      configPRINTF( ( "Webpage /m0 detected\n" ) );
    }

    // If "GET /px=v" command, set port PEx(0/1/3) with value v
    if (strcmp(LGetParameter, "p") == 0)
    {
      LRequestPath = 'p';
      configPRINTF( ( "Webpage /px=v detected\n" ) );
    }

    // If "GET /r" command, reset counters
    if (strcmp(LGetParameter, "r") == 0)
    {
      LRequestPath = 'r';
      TaskWebServer_SomeCounter = 0;
      configPRINTF( ( "Webpage /r detected\n" ) );
    }

    // If "GET /z" command, reset system
    if (strcmp(LGetParameter, "z") == 0)
    {
      LRequestPath = 'z';
      TaskWebServer_ResetDevice = true;
      configPRINTF( ( "Webpage /z detected\n" ) );
    }

    if (LRequestPath != 0)
      LState = 2;
    else
      return -1;
  }
  else
  {
    if (LLength > 0) {
      configPRINTF( ( "Invalid data\n" ) );
    }
  }

  while (true)
  {
    configPRINTF( ( "State %d\n", LState ) );
    switch (LState)
    {
      case 0:   LState = 2;
                break;
      // Start building the output
      case 2:   // Copy header
                LSize  = 0;
                LSize  += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_HEADER, 0, 1, "WebServer", "");
                LState = 3;
                break;
      // Copy body
      case 3:   if (LRequestPath == 'e' + 'c')
                {
                  // If "GET /ec" command, redirect to /e page
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_REDIRECT, 0, "/e", "exception");
                }
                else if (LRequestPath == 'e')
                {
                  // If "GET /e" command, display exception page
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_MENU);
                  //size += Exception_Table(buffer_out+size, MaxSize-size-41);
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, "<p><a href='/ec'>Clear Exceptions</a></p>");
                }
                else if (LRequestPath == 'j')
                {
                  // If "GET /j" command, display jtag page
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_WRITE);
                }
                else if (LRequestPath == 'r')
                {
                  // If "GET /r" command, show home page
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_REDIRECT, 0, "/", "home");
                }
                else if (LRequestPath == 'z')
                {
                  // If "GET /z" command, show home page
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_REDIRECT, 0, "/", "home");
                }
                else if (LRequestPath == 'd')
                {
                  // If "GET /d" command, display debug page
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_MENU);
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, "<h3>DEBUG</h3>");
                  // Add watch register information
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, "<p><a href='/r'>Reset counters</a></p>");
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, "<p><a href='/z'>Reset system</a></p>");
                }
                else
                {
                  // Display default page
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_MENU);
                  // Copy print information
#ifdef DEBUG
  #ifdef FREERTOS_PLUS_TCP
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_DEVICE_INFO, tskKERNEL_VERSION_NUMBER, ipFR_TCP_VERSION_NUMBER, " DEBUG", "DeviceName", "");
  #endif
  #ifdef FREERTOS_PLUS_LWIP
    #ifdef FREERTOS_PLUS_OPCUA
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_DEVICE_INFO, tskKERNEL_VERSION_NUMBER, LWIP_VERSION_STRING, UA_OPEN62541_VERSION, " DEBUG", "DeviceName", "");
    #else
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_DEVICE_INFO, tskKERNEL_VERSION_NUMBER, LWIP_VERSION_STRING, " DEBUG", "DeviceName", "");
    #endif
  #endif
#else
  #ifdef FREERTOS_PLUS_TCP
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_DEVICE_INFO, tskKERNEL_VERSION_NUMBER, ipFR_TCP_VERSION_NUMBER, " RELEASE", "DeviceName", "");
  #endif
  #ifdef FREERTOS_PLUS_LWIP
    #ifdef FREERTOS_PLUS_OPCUA
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_DEVICE_INFO, tskKERNEL_VERSION_NUMBER, LWIP_VERSION_STRING, UA_OPEN62541_VERSION, " RELEASE", "DeviceName", "");
    #else
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_DEVICE_INFO, tskKERNEL_VERSION_NUMBER, LWIP_VERSION_STRING, " RELEASE", "DeviceName", "");
    #endif
  #endif
#endif
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, "<br/><b>Running services:</b><table>");
#ifdef DEBUG
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, "<tr><td>Serial port</td><td>:</td><td>Active (debug)</td></td></tr>");
#else
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, "<tr><td>Serial port</td><td>:</td><td>Not active (release)</td></td></tr>");
#endif
                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, "</table>");

                  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize,
                    "<br/><table>"
                    "<tr><td>Device identifier</td><td>:</td><td>%s</td></tr>"
                    "<tr><td>System up time</td><td>:</td><td>%d seconds</td></tr>"
                    "<tr><td>Random number</td><td>:</td><td>%d</td></tr>"
                    "<tr><td>Refreshes</td><td>:</td><td>%d</td><td>%s</td></tr>"
                    "<tr><td>Last error</td><td>:</td><td>%s</td></tr>"
                    "</table>",
                    "SystemIdText", xTaskGetTickCount()/1000, urand(), TaskWebServer_SomeCounter++, "RestartsText", "LastErrorText");
                }
                LState  = 10;
                break;
      // Copy footer (last stage before actual sending the data)
      case 10:  LSize += SystemWriteToBuffer(LBufferOut+LSize, LMaxSize-LSize, HTML_FOOTER);
                return LSize;
                break;
      default:  return -1;
    }
  }
}
