package me.konloch.kontainer.io;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

/**
 * A wrapper for Java SE classes to write/read an HTTP Request
 *
 * @author Konloch
 */

public class HTTPRequest {

    public URL url;
    private int timeout = 30000;
    private String cookie;
    private String referer;
    private String postData;
    private String useragent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0";
    private Proxy proxy;
    private boolean setFollowRedirects = true;
    private BufferedReader reader;
    private DataOutputStream writer;
    private HttpURLConnection connection;
    private Set<Entry<String, List<String>>> lastConnectionHeaders;

    /**
     * Creates a new HTTPRequest object
     *
     * @param url
     */
    public HTTPRequest(URL url) {
        this.url = url;
    }

    /**
     * Sets a referer to send to the web server
     */
    public void setReferer(String referer) {
        this.referer = referer;
    }

    /**
     * Set a cookie string to send to the web server
     */
    public void setCookie(String cookie) {
        this.cookie = cookie;
    }

    /**
     * Sets post data to send to the web server
     */
    public void setPostData(String postData) {
        this.postData = postData;
    }

    /**
     * Sets a custom useragent, default 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0'
     */
    public void setUseragent(String useragent) {
        this.useragent = useragent;
    }

    /**
     * Sets the seconds till timeout, default 30,000 milliseconds
     */
    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    /**
     * Sets a proxy to connect through
     */
    public void setProxy(Proxy proxy) {
        this.proxy = proxy;
    }

    /**
     * Used to get the headers the webserver sent on our last connection
     */
    public Set<Entry<String, List<String>>> getLastConnectionHeaders() {
        return lastConnectionHeaders;
    }

    /**
     * By default follow redirects are enabled
     */
    public void setFollowRedirects(boolean setFollowRedirects) {
        this.setFollowRedirects = setFollowRedirects;
    }

    /**
     * Used to set up the connection to read the content.
     */
    private void setup() throws Exception {
        if (proxy != null)
            connection = (HttpURLConnection) url.openConnection(proxy);
        else
            connection = (HttpURLConnection) url.openConnection();

        if (cookie != null)
            connection.setRequestProperty("Cookie", cookie);
        if (referer != null)
            connection.addRequestProperty("Referer", referer);

        connection.setRequestProperty("User-Agent", useragent);
        connection.setReadTimeout(timeout);
        connection.setConnectTimeout(timeout);
        connection.setUseCaches(false);
        HttpURLConnection.setFollowRedirects(setFollowRedirects);

        if (postData != null) {
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setDoInput(true);
            writer = new DataOutputStream(connection.getOutputStream());
            writer.writeBytes(postData);
            writer.flush();
        }

        reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    }

    /**
     * Reads the entire page and returns a string array
     *
     * @return
     * @throws Exception
     */
    public String[] read() throws Exception {
        ArrayList<String> st;

        try {
            setup();

            st = new ArrayList<String>();
            String s;
            while ((s = reader.readLine()) != null)
                st.add(s);

            lastConnectionHeaders = connection.getHeaderFields().entrySet();
        } catch (Exception e) {
            cleanup();
            throw e;
        } finally {
            cleanup();
        }

        return st.toArray(new String[st.size()]);
    }

    /**
     * Reads as many lines as expected unless it reaches the end.
     *
     * @param linesToRead
     * @return
     * @throws Exception
     */
    public String[] read(int linesToRead) throws Exception {
        ArrayList<String> st;

        try {
            setup();

            st = new ArrayList<String>();
            for (int i = 0; i < linesToRead; i++) {
                String s = reader.readLine();
                if (s != null)
                    st.add(s);
            }

            lastConnectionHeaders = connection.getHeaderFields().entrySet();
        } catch (Exception e) {
            cleanup();
            throw e;
        } finally {
            cleanup();
        }

        return st.toArray(new String[st.size()]);
    }

    /**
     * Only reads the first line
     *
     * @return
     * @throws Exception
     */
    public String readSingle() throws Exception {
        String s;

        try {
            setup();

            s = reader.readLine();

            lastConnectionHeaders = connection.getHeaderFields().entrySet();
        } catch (Exception e) {
            cleanup();
            throw e;
        } finally {
            cleanup();
        }

        return s;
    }

    /**
     * Reads until it reaches the expected line then it returns it.
     *
     * @param linesToRead
     * @return
     * @throws Exception
     */
    public String readSingle(int linesToRead) throws Exception {
        String s;

        try {
            setup();

            for (int i = 0; i < linesToRead - 1; i++)
                reader.readLine();

            s = reader.readLine();

            lastConnectionHeaders = connection.getHeaderFields().entrySet();
        } catch (Exception e) {
            cleanup();
            throw e;
        } finally {
            cleanup();
        }

        return s;
    }

    /**
     * Used to clean up the connection, closes the connections and nulls the objects
     */
    private void cleanup() {
        try {
            reader.close();
        } catch (Exception e) {
        }
        try {
            writer.close();
        } catch (Exception e) {
        }
        try {
            connection.disconnect();
        } catch (Exception e) {
        }
        reader = null;
        writer = null;
        connection = null;
    }

}