import java.net.*;
import java.io.*;
import java.util.*;
import javax.net.*;
import javax.net.ssl.*;
import javax.swing.*;

/** Wewnętrzny klient sieciowy używany przez WebClient. Wysyła
 *  żądanie HTTP w następującym formacie:<P>
 *
 *  GET / HTTP/1.0
 *  <P>
 *  Metodą może być GET lub POST, a wersją HTTP może być 1.0
 *  or 1.1 (nagłówek Host: jest wymagany dla HTTP 1.1).
 *  Obsługuje połączenia HTTP i HTTPS (SSL).
 *  <P>
 *  Taken from Core Servlets and JavaServer Pages Volume II
 *  from Prentice Hall and Sun Microsystems Press,
 *  http://volume2.coreservlets.com/.
 *  (C) 2007 Marty Hall, Larry Brown, and Yaakov Chaikin;
 *  may be freely used or adapted.
 */

public class HttpClient {
  protected URL url;
  protected String requestMethod;
  protected String httpVersion;
  protected List requestHeaders;
  protected String queryData;
  protected JTextArea outputArea;
  protected boolean interrupted;

  public HttpClient(URL url,
                    String requestMethod,
                    String httpVersion,
                    List requestHeaders,
                    String queryData,
                    JTextArea outputArea) {
    this.url = url;
    this.requestMethod = requestMethod;
    this.httpVersion = httpVersion;
    this.requestHeaders = requestHeaders;
    this.queryData = queryData;
    this.outputArea = outputArea;
  }

  /** Zestawienie połączenia i przekazanie gniazda
   *  do handleConnection.
   */

  public void connect() {
    if(!isValidURL()) {
      return;
    }
    String host = url.getHost();
    int port = url.getPort();
    if (port == -1) {
      port = url.getDefaultPort();
    }
    connect(host, port);
  }

  /** Otwarcie połączenia TCP do komputera na wskazanym porcie
   *  i wywołanie handleConnection, aby przetworzyć żądanie.
   *  Dla żądania https używane jest gniazdo SSL.
   */

  protected void connect(String host, int port) {
    try {
      Socket client = null;
      if (isSecure()) {
        SocketFactory factory = SSLSocketFactory.getDefault();
        client = factory.createSocket(host, port);
      } else {
        client = new Socket(host, port);
      }
      handleConnection(client);
      client.close();
    } catch(UnknownHostException uhe) {
      report("Nieznany komputer: " + host);
      uhe.printStackTrace();
    } catch(ConnectException ce) {
      report("Problem z połączeniem: " + ce.getMessage());
      ce.printStackTrace();
    } catch(IOException ioe) {
      report("IOException: " + ioe.getMessage());
      ioe.printStackTrace();
    }
  }

  /** Wysłanie żądania do serwera ze wszystkimi nagłówkami
   *  i danymi zapytania. Do żądań POST dodanie nagłówka
   *  Content-Length.
   */

  public void handleConnection(Socket socket) {
    try {
      // Utworzenie PrintWriter do wysyłania danych wyjściowych.
      // Drugi argument o wartości true oznacza automatyczne opróżnienie.
      PrintWriter out =
        new PrintWriter(socket.getOutputStream(), true);
      // Utworzenie BufferedReader do przyjmowania danych przychodzących.
      BufferedReader in =
        new BufferedReader(
          new InputStreamReader(socket.getInputStream()));
      StringBuffer buffer = new StringBuffer();
      outputArea.setText("");
      buffer.append(getRequestLine() + "\r\n");
      for(int i=0; i<requestHeaders.size(); i++) {
        buffer.append(requestHeaders.get(i) + "\r\n");
      }
      // Dodanie nagłówka Content-Length do danych POST.
      if ("POST".equalsIgnoreCase(requestMethod)) {
        buffer.append("Content-Length: " +
                      queryData.length() + "\r\n");
        buffer.append("\r\n");
        buffer.append(queryData);
      } else {
        buffer.append("\r\n");
      }
      System.out.println("Żądanie:\n\n" + buffer.toString());
      out.println(buffer.toString());
      out.flush();
      String line;
      while ((line = in.readLine()) != null &&
             !interrupted) {
        outputArea.append(line + "\n");
      }
      if (interrupted) {
        outputArea.append("---- Pobieranie przerwane ----");
      }
      out.close();
      in.close();
    } catch(Exception e) {
      outputArea.setText("Błąd: " + e);
    }
  }

  /** Utworzenie wiersza żądania HTTP, tzn. GET URI HTTP/1.0 */

  protected String getRequestLine() {
    String method = "GET";
    String uri = url.getPath();
    String version = "HTTP/1.0";
    // Sprawdzenie, czy metodą HTTP jest POST. Jeśli nie, jest to żądanie GET.
    // Dodanie do żądania GET danych zapytania po znaku ?.
    if ("POST".equalsIgnoreCase(requestMethod)) {
      method = "POST";
    } else {
      if (queryData.length() > 0) {
        uri += "?" + queryData;
      }
    }
    if ("HTTP/1.1".equalsIgnoreCase(httpVersion)) {
      version = "HTTP/1.1";
    }
    String request = method + " " + uri + " " + version;
    return(request);
  }

  protected void report(String str) {
    outputArea.setText(str);
  }

  /* Sprawdzenie protokołu dla https (SSL). */

  protected boolean isSecure() {
    return("https".equalsIgnoreCase(url.getProtocol()));
  }

  public void setInterrupted(boolean interrupted) {
    this.interrupted = interrupted;
  }

  /** Sprawdzenie, czy komputer odpowiada adresowi internetowemu. */

  protected boolean isValidURL() {
    if (url == null) {
      return(false);
    }
    try {
      InetAddress.getByName(url.getHost());
      return(true);
    } catch(UnknownHostException uhe) {
      report("Bogus Host: " + url.getHost());
      return(false);
    }
  }
}