import java.net.Socket;
import java.net.ServerSocket;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ServerSocketFactory;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLServerSocket;

public class Client
{
  private int port = 5000;
  private int iterations = 10;
  private String host = "localhost";

  public Client(String ip, int port,int iterations)
  {
    host = ip;
    this.port = port;
    this.iterations = iterations;
  }

  private void connect()
  {
    connect(host, port);
  }

  private void connect(String ip_, int port_)
  {
      SocketFactory socketFactory = SSLSocketFactory.getDefault();
      Socket socket = null;
      InputStream in = null;
      InputStreamReader isr = null;
      BufferedReader br = null;
      OutputStream out = null;
      OutputStream os = null;
      OutputStreamWriter osw = null;
      BufferedWriter bw = null;
      try {
        socket = socketFactory.createSocket(ip_,port_);
        //Machinary that lets me read from the socket
        in = socket.getInputStream();
        isr = new InputStreamReader(in);
        br = new BufferedReader(isr);
        //Machinary that lets me write to my socket...
        out = socket.getOutputStream();
        osw  = new OutputStreamWriter(out);
        bw = new BufferedWriter(osw);
	//Call method that nadles communications.
        handleCommunication(br,bw);
        br.close();
        isr.close();
        in.close();
        bw.close();
        osw.close();
        out.close();
        socket.close();
      } catch(Exception e) {
        e.printStackTrace();
      } finally {
        try {
          if(br != null) br.close();
          if(isr != null) isr.close();
          if(in != null) in.close();
          if(bw != null) bw.close();
          if(osw != null) osw.close();
          if(out != null) out.close();
          if(socket != null) socket.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
  }

  private void handleCommunication(BufferedReader br, BufferedWriter bw)
        throws IOException, Exception
  {
    bw.write("<<<<<\n");
    bw.flush();
    String line = br.readLine();
    System.out.println("Line: " + line);
    //Begin handshake
    if( line != null && line.equals(">>>>>"))
    {
      System.out.println(">>>>> encountered as expected.");
    } else {
      throw new IOException("Protocol Violation: leading '>>>>>' not found");
    }
    System.out.println("Handshake completed successfully");
    System.out.println("Iterations: " + iterations);
    StringBuffer strBuf = new StringBuffer();
    strBuf.append(iterations);
    strBuf.append("\n");
    bw.write(strBuf.toString());
    bw.flush();
    line = null;
    line = br.readLine();
    System.out.println("Line: " + line);
    if(line != null && (line.equals("DENIED") || line.equals("ACCEPT"))) {
      System.out.println("Received valid response.");
    } 
    if(line.equals("DENIED"))
    {
      System.out.println("Server cannot fulfil request at this time");
      throw new Exception("Protocol Exception: received DENIED from server"); 
    } else {
      System.out.println("Server will count to " + iterations + " with me.");
    }
    for(int i = 1; i <= iterations; i++) 
    {
      strBuf.setLength(0);
      strBuf.append(i);
      strBuf.append("\n");
      bw.write(strBuf.toString());
      bw.flush();
      System.out.println("Sent: " + strBuf.toString());
      line = null;
      line = br.readLine();
      System.out.println("Received: " + line);
      if(strBuf.toString().equals(line + "\n")) 
      {
        System.out.println("Response Message matches");
      } else {
        throw new Exception("Response Message did NOT match: " + line + " vs. " + strBuf.toString());
      }
    }
  }
  
  public static void main(String []args)
  {
    Client client = new Client(args[0],Integer.parseInt(args[1]),Integer.parseInt(args[2]));
    client.connect();
  }
}
