/*
   This client lets you send arbitrary HTTP commands
   and HTTP request headers to the HTTP server. You
   need to be sure to imput properly formatted HTTP
   request lines and request headers.

   This client can be used to test a persistent
   connection to a server. Use the
      Connection: close
   request header to end the persistent connection.

   This client takes two optional command-line arguments,
      hostname  port

   This client defaults to connecting to the
   local web server defined in this folder.
*/

import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.IOException;
import java.net.UnknownHostException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

public class HttpClient_v2
{
   private static final int SERVER_PORT = 8080;

   public static void main (String[] args)
   {
      final String hostName;
      if (args.length > 0)
      {
         hostName = args[0];
      }
      else
      {
         hostName = "localhost";
      }

      final int portNumber;
      if (args.length > 1)
      {
         portNumber = Integer.parseInt(args[1]);
      }
      else
      {
         portNumber = SERVER_PORT;
      }

      // Get this client's process id number (PID). This helps
      // to identify the client in TaskManager or TCPView.
      final ProcessHandle handle = ProcessHandle.current();
      final long pid = handle.pid();
      System.out.println("CLIENT: Process ID number (PID): " + pid );

      // Get the name and IP address of the local host and
      // print them on the console for information purposes.
      try
      {
         final InetAddress address = InetAddress.getLocalHost();
         System.out.println("CLIENT: Hostname: " + address.getCanonicalHostName() );
         System.out.println("CLIENT: IP address: " +address.getHostAddress() );
      }
      catch (UnknownHostException e)
      {
         System.out.println("Unable to determine this client's address.");
         System.out.println( e );
      }

      // Make a connection to the server.
      Socket         socket = null;
      BufferedReader in = null;
      PrintWriter    out = null;
      try
      {
         System.out.println("CLIENT: Connecting to server: " + hostName
                          + " on port " + portNumber );

         socket = new Socket(InetAddress.getByName(hostName),
                             portNumber);

         in = new BufferedReader(
                  new InputStreamReader(
                       socket.getInputStream()));

         out = new PrintWriter(socket.getOutputStream());
      }
      catch (IOException e)
      {
         System.out.println("CLIENT: Cannot connect to server.");
         e.printStackTrace();
         System.exit(-1);
      }
      System.out.println("CLIENT: Connected to server.");
      // Get this client's local port number and log it to the console.
      // This helps to identify this client in the server's transcript.
      final int port = socket.getLocalPort();
      System.out.println("CLIENT: Local Port: " + port);


      // Implement the HTTP client/server application level protocol.
      // Send the server an HTTP request.
      final Scanner stdin = new Scanner(System.in);
      boolean keepAlive = true;
      while (keepAlive)
      {
         System.out.print("Request line: ");
         final String requestLine;
         if ( stdin.hasNextLine() )
         {
            requestLine = stdin.nextLine();
         }
         else
         {
            requestLine = null;
            break;
         }
         out.println(requestLine);
         out.flush();

         System.out.print("Request header: ");
         String requestHeader = stdin.nextLine();
         while ( ! requestHeader.isEmpty() )
         {
            if ( requestHeader.startsWith("Connection") )
            {
               final int i = requestHeader.indexOf(':');
               final String value = requestHeader.substring(i+1).trim();
               if ( value.equals("close") )
               {
                  keepAlive = false;
               }
            }
            out.println(requestHeader);
            out.flush();
            System.out.print("Request header: ");
            requestHeader = stdin.nextLine();
         }
         out.print("\r\n"); // The sentinel value that marks the end of the http headers.
         out.flush();

         // Receive the HTTP response from the server (including the response headers).
         try
         {
            String responseHeader;
            int entityLength = 0;
            // Read the response headers.
            while ( (responseHeader = in.readLine()) != null )
            {
               System.out.println(">" + responseHeader);

               if ( responseHeader.startsWith("Connection") )
               {
                  final int i = responseHeader.indexOf(':');
                  final String value = responseHeader.substring(i+1).trim();
                  if ( value.equals("close") )
                  {
                     keepAlive = false;
                  }
               }
               else if ( responseHeader.startsWith("Content-Length") )
               {
                  final int i = responseHeader.indexOf(':');
                  entityLength = Integer.parseInt(responseHeader.substring(i+2));
                  // Log the value of Content-Length
                  //System.out.println("===> Content-Length = " + entityLength);
               }
               else if ( responseHeader.isEmpty() )
               {
                  break;
               }
            }

            // Read the entity body.
            char[] entityBodyChars = new char[entityLength];
            int totalBytesRcvd = 0;  // Total bytes received so far.
            int bytesRcvd;           // Bytes received in last read.
            while ( totalBytesRcvd < entityLength )
            {
               //System.out.println("===> Reading entity body.");
               bytesRcvd = in.read(entityBodyChars,
                                   totalBytesRcvd,
                                   entityLength - totalBytesRcvd);
               if (-1 == bytesRcvd)
               {
                  keepAlive = false;
                  break; // reached end-of-file
               }
               totalBytesRcvd += bytesRcvd;
               //System.out.println("===> totalBytesRcvd = " + totalBytesRcvd);
            }

            // Log the entity body to stdout.
            System.out.println(new String(entityBodyChars));
         }
         catch (IOException e)
         {
            System.out.println("CLIENT: Cannot receive response from server.");
            System.out.println( e );
         }
      }

      // Disconnect from the server.
      try
      {
         socket.close();
         System.out.println("CLIENT: Closed socket.");
      }
      catch (IOException e)
      {
         System.out.println("CLIENT: Cannot disconnect from server.");
         System.out.println( e );
      }
   }
}
