/*
   This client implements only PUT.

   This client takes

      fileName path hostname port

   as optional command-line arguments. The
   contents of "fileName" are uploaded to
   the server, by the PUT method, to the
   resource named "path".

   NOTE: The HTTP PUT and DELETE methods are not supported by HTML forms.
         https://alexanderpetros.com/triptych/form-http-methods
   To test our implementations of the PUT and DELETE methods we need to
   either write a Java client or embed some JavaScript code into a web
   page.
*/

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.UnknownHostException;
import java.net.InetAddress;
import java.net.Socket;

public class HttpClient_PUT
{
   private static final int SERVER_PORT = 8080; // Should be above 1023.

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

      final String path;
      if (args.length > 1)
      {
         path = args[1];
      }
      else
      {
         path = "upLoad_1";
      }

      final String hostName;
      if (args.length > 2)
      {
         hostName = args[2];
      }
      else
      {
         hostName = "localhost";
      }

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

      // Check for the existence of the file to PUT.
      final File file = new File(fileName);
      if (! file.exists())
      {
         System.out.println("CLIENT: File, " + fileName + ", not found.");
         System.exit(-1);
      }
      if (! file.canRead())
      {
         System.out.println("CLIENT: File, " + fileName + ", is not accessible.");
         System.exit(-1);
      }
      final int numOfBytes = (int)file.length();

      // Read the file from the file system.
      final byte[] fileInBytes = new byte[numOfBytes];
      try (final var inFile = new FileInputStream(file))
      {
         // https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/DataInputStream.html#read(byte%5B%5D)
         inFile.read(fileInBytes);
      }
      catch (FileNotFoundException e)
      {
         System.out.println("CLIENT: Unable to find file:" + fileName);
         System.exit(-1);
      }
      catch (IOException e)
      {
         System.out.println("CLIENT: Unable to read file:" + fileName);
         System.exit(-1);
      }

      // 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.
      System.out.println("CLIENT: Connecting to server: " + hostName
                       + " on port " + portNumber );

      try (final Socket socket = new Socket(InetAddress.getByName(hostName),
                                            portNumber);
           final var in = new BufferedReader(
                             new InputStreamReader(
                                socket.getInputStream()));
           final var out = new DataOutputStream(socket.getOutputStream()))
         {
         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 an HTTP PUT transaction.
         // Send the server the request headers.
         try
         {
                   out.writeBytes("PUT " + path + " HTTP/1.1\r\n");
            System.out.println("<PUT " + path + " HTTP/1.1");
                   out.writeBytes("Host: " + hostName + ":" + portNumber + "\r\n");
            System.out.println("<Host: " + hostName + ":" + portNumber);
                   out.writeBytes("Connection: close\r\n");
            System.out.println("<Connection: close");
            final String contentType;
            if ( fileName.endsWith(".html")
              || fileName.endsWith(".htm") )
               contentType = "text/html";
            else if ( fileName.endsWith(".css") )
               contentType = "text/css";
            else if ( fileName.endsWith(".js") )
               contentType = "application/javascript";
            else if ( fileName.endsWith(".jpg")
                   || fileName.endsWith(".jpeg") )
               contentType = "image/jpeg";
            else if ( fileName.endsWith(".gif") )
               contentType = "image/gif";
            else if ( fileName.endsWith(".png") )
               contentType = "image/png";
            else if ( fileName.endsWith(".pdf") )
               contentType = "application/pdf";
            else if ( fileName.endsWith(".txt") )
               contentType = "text/plain";
            else  // default content type
               contentType = "application/octet-stream";
            out.writeBytes("Content-Type: " + contentType + "\r\n");
            System.out.println("<Content-Type: " + contentType);
            out.writeBytes("Content-Length: " + numOfBytes + "\r\n");
            System.out.println("<Content-Length: " + numOfBytes);
            // Add a blank line to denote end of request headers.
            out.writeBytes("\r\n");
            System.out.println("<");
                   out.flush();
         }
         catch (IOException e)
         {
            System.out.println("CLIENT: Cannot communicate with server.");
            e.printStackTrace();
            System.exit(-1);
         }

         // Send the server the request body (the file to PUT).
         // https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/DataOutputStream.html#write(int)
         out.write(fileInBytes);
         out.flush();
         System.out.println("<=== File " + fileName + " sent to Server");

         // Receive the response from the server.
         try
         {
            String oneLine;
            // Read the response line and the response headers.
            while ( (oneLine = in.readLine()) != null ) // read up to eof or a blank line
            {
               System.out.println(">" + oneLine);
               if ( oneLine.isEmpty() )
               {
                  break;
               }
            }
            // Read the entity body (if there is one).
            while ( (oneLine = in.readLine()) != null ) // read up to eof
            {
               System.out.println(oneLine);
            }
         }
         catch (IOException e)
         {
            System.out.println("CLIENT: Cannot receive response from server.");
            System.out.println( e );
         }
      }
      catch (IOException e)
      {
         System.out.println("CLIENT: Cannot connect to server.");
         e.printStackTrace();
         System.exit(-1);
      }
      System.out.println("CLIENT: Closed socket.");
   }
}
