#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

void fErrorExit(char * errStr)
{
  fprintf(stderr, "\nError: %s\n", errStr);
  exit(-1);
}

int fMenue()
{
  int rc = 0;
  do {
    fprintf(stdout, "\nHow should we reply?\n");
    fprintf(stdout, "1 200 OK (with sample page)\n");
    fprintf(stdout, "2 401 Unauthorized (with error report)\n");
    fprintf(stdout, "3 404 Not found (with error report)\n");
    fprintf(stdout, "4 501 Not implemented (with error report)\n");
    fprintf(stdout, "5 500 Internal Server Error (with error report)\n");
    fprintf(stdout, "6 close connection immediately\n");

    fscanf(stdin, "%d", &rc);
  } while ((rc<1)||(rc>6));
  return(rc);
}

int main(int argc, char* argv[])
{

  if (argc!=2)
    fErrorExit("usage: myWebServer port");
  int portnum = atoi(argv[1]);

  int s; // socket 
  if ((s=socket(PF_INET,SOCK_STREAM,0))<0)
  {
    fErrorExit("could not create socket");
  }

  struct sockaddr_in sockAddr;

  sockAddr.sin_family = AF_INET;
  sockAddr.sin_port = htons(portnum);
  sockAddr.sin_addr.s_addr = INADDR_ANY;

  if (bind(s, (struct sockaddr *)&sockAddr, sizeof(sockAddr))<0)
  {
    fErrorExit("could not bind");
  }

  int backlog = 8;
  if (listen(s, backlog)<0) 
    fErrorExit("Could not listen");

  unsigned int addrLen = sizeof(sockAddr);
  int t; // new socket to get from accept
  // Web server routine
  // wait for connection
  // serialized operation: only one connection can be accepted at one time
  while (true) 
  {
    if ((t = accept(s, (struct sockaddr *)&sockAddr, &addrLen))<0)
    {
      fErrorExit("Could not accept connection");
    }
    fprintf(stdout, "Connection from %s:%d.\n", 
      inet_ntoa(sockAddr.sin_addr), ntohs(sockAddr.sin_port));

    // get request string from client
    char msgStr[1024];
    int nChar = 0;
    if ((nChar = read(t, msgStr, sizeof(msgStr)))>0)
    {
      // trim the string
      msgStr[nChar]='\0';
      // cccc -------------------------------------------------------------
      // cccc | currently this is an extremely simple hack that only works 
      // cccc | with buffered input!
      // cccc | should search for CRLF sequence to detect commands instead
      // cccc -------------------------------------------------------------
      fprintf(stdout, "received %d characters: \n'%s'\n", nChar, msgStr);
      // select reply string interactively from menue
      int m = fMenue();
      switch(m)
      {
        case 1 : 
          // 200 OK with sample page
          {
            char * replyStr = 
              "HTTP/1.0 200 OK\n\n<HTML>\n<HEAD><TITLE>Sample Page</TITLE></HEAD>\n<BODY BGCOLOR=\"#FFFFFF\">\n<H1>Sample Page</H1>This is a sample page with some sample text...\n</BODY>\n</HTML>";
            if (write(t, replyStr, strlen(replyStr)) < 0)
              fErrorExit("Could not write echo string");
          }
          break;
        case 2 : 
          // 401 unauthorized
          {
            char * replyStr = 
              "HTTP/1.0 401 Unauthorized\n\n<HTML>\n<HEAD><TITLE>401 Unauthorized Access</TITLE></HEAD>\n<BODY BGCOLOR=\"#CCCCFF\">\n<H1>Unauthorized Access</H1>You do not have sufficient privileges to access this page\n</BODY>\n</HTML>";
            if (write(t, replyStr, strlen(replyStr)) < 0)
              fErrorExit("Could not write echo string");
          }
          break;
        case 3 : 
          // 404 not found with sample page
          {
            char * replyStr = 
              "HTTP/1.0 404 Not Found\n\n<HTML>\n<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n<BODY BGCOLOR=\"#FFCCFF\">\n<H1>Not Found</H1>The page you requested was not found on this server\n</BODY>\n</HTML>";
            if (write(t, replyStr, strlen(replyStr)) < 0)
              fErrorExit("Could not write echo string");
          }
          break;
        case 4 : 
          // 501 not implemented with sample page
          {
            char * replyStr = 
              "HTTP/1.0 501 Not Implemented\n\n<HTML>\n<HEAD><TITLE>501 Not Implemented</TITLE></HEAD>\n<BODY BGCOLOR=\"#FFCCCC\">\n<H1>Not Implemented</H1>The requested method is not implemented in this server.\n</BODY>\n</HTML>";
            if (write(t, replyStr, strlen(replyStr)) < 0)
              fErrorExit("Could not write echo string");
          }
          break;
        case 5 : 
          // 500 internal server error with sample page
          {
            char * replyStr = 
              "HTTP/1.0 500 Internal Server Error\n\n<HTML>\n<HEAD><TITLE>Internal Server Error</TITLE></HEAD>\n<BODY BGCOLOR=\"#CCFFFF\">\n<H1>Sorry</H1>Due to an internal error, this server was not able to fulfill your request.\n</BODY>\n</HTML>";
            if (write(t, replyStr, strlen(replyStr)) < 0)
              fErrorExit("Could not write echo string");
          }
          break;
        case 6 : 
        default : 
          // close connection immediately
          break;
      }
    }
    // close accepted socket
    if (close(t)<0)
      fErrorExit("Could not close accepted socket");
  }



  // close original socket
  if (close(s)<0)
    fErrorExit("Could not close original socket");

  return(0);
}

