import com.jscape.inet.mft.resources.FtpResourceDescriptor;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;

/**
 * This example demonstrates how to create a FTP reverse proxy using the server API.
 */
public class CreateResourceExample {

    private static final BufferedReader CONSOLE_READER = new BufferedReader(new InputStreamReader(System.in));

    private static final String LOGIN_URL_TEMPLATE = "http://%s:%s/rest/management/login";
    private static final String LOGOUT_URL_TEMPLATE = "http://%s:%s/rest/management/logout";
    private static final String ADD_PROXY_URL_TEMPLATE = "http://%s:%s/rest/management/reverse-proxies/%s";

    private static final String LOGIN_TEMPLATE = "{\"username\":\"%s\", \"password\":\"%s\"}";

    private static final String FTP_REVERSE_PROXY_TEMPLATE =
            "{\"protocol\":\"FTP\","
            + "\"name\":\"%s\","
            + "\"host\":\"%s\","
            + "\"port\":%d,"
            + "\"connectionType\":\"%s\","
            + "\"username\":\"%s\","
            + "\"password\":\"%s\","
            + "\"passive\":%s,"
            + "\"usingCredentials\":false,"
            + "\"timeout\":0,"
            + "\"keyProvider\":null,"
            + "\"logDirectory\":null,"
            + "\"timeToLive\":null,"
            + "\"remoteDirectory\":{\"@type\":\"REMOTE\"}}";

    private static final String CHARSET = "UTF-8";

    private final String host;
    private final int port;
    private final String username;
    private final String password;
    private final String domainName;
    private final String reverseProxyName;
    private final String reverseProxyHost;
    private final int reverseProxyPort;
    private final String reverseProxyConnectionType;
    private final String reverseProxyUsername;
    private final String reverseProxyPassword;
    private String sessionId;

    /**
     * The application entry point.
     *
     * @param arguments command line arguments
     * @throws Exception if an error occurs
     */
    public static void main(String[] arguments)
            throws Exception {
        showExampleTitle();
        CreateResourceExample example = createExample();
        example.execute();
    }

    /**
     * Shows example title.
     */
    private static void showExampleTitle() {
        System.out.println("This example demonstrates how to create a FTP reverse proxy using the server API.");
        System.out.println();
    }


    /**
     * Creates an example instance.
     *
     * @return an example instance
     *
     * @throws Exception if an error occurs
     */
    private static CreateResourceExample createExample()
            throws Exception {
        String host = getParameter("Enter server REST host: ");
        int port = Integer.parseInt(getParameter("Enter server REST port: "));
        String username = getParameter("Enter username: ");
        String password = getParameter("Enter password: ");
        String domainName = getParameter("Enter domain name: ");
        String reverseProxyName = getParameter("Enter reverse proxy name: ");
        String reverseProxyHost = getParameter("Enter reverse proxy hostname: ");
        int reverseProxyPort = Integer.parseInt(getParameter("Enter reverse proxy port: "));

        String reverseProxyConnectionType = "";
        while (!reverseProxyConnectionType.equals(FtpResourceDescriptor.REGULAR)
               && !reverseProxyConnectionType.equals(FtpResourceDescriptor.EXPLICIT_SSL)
               && !reverseProxyConnectionType.equals(FtpResourceDescriptor.IMPLICIT_SSL)) {
            reverseProxyConnectionType = getParameter("Enter reverse proxy type (regular, explicit SSL, implicit SSL): ");
        }

        String reverseProxyUsername = getParameter("Enter reverse proxy username: ");
        String reverseProxyPassword = getParameter("Enter reverse proxy password: ");

        return new CreateResourceExample(host,
                                         port,
                                         username,
                                         password,
                                         domainName,
                                         reverseProxyName,
                                         reverseProxyHost,
                                         reverseProxyPort,
                                         reverseProxyConnectionType,
                                         reverseProxyUsername,
                                         reverseProxyPassword);
    }

    /**
     * Support method. Shows prompt on console and reads user response.
     *
     * @param prompt prompt string
     *
     * @return user response
     *
     * @throws Exception if an I/O error occurs
     */
    private static String getParameter(String prompt)
            throws Exception {
        System.out.print(prompt);
        return CONSOLE_READER.readLine();
    }

    /**
     * Constructs example.
     *
     * @param host server REST host
     * @param port server REST port
     * @param username server username
     * @param password server password
     * @param domainName domain name
     * @param reverseProxyName resource name
     * @param reverseProxyHost resource hostname
     * @param reverseProxyPort resource port
     * @param reverseProxyConnectionType resource connection type
     * @param reverseProxyUsername resource username/login
     * @param reverseProxyPassword password
     */
    public CreateResourceExample(String host,
                                 int port,
                                 String username,
                                 String password,
                                 String domainName,
                                 String reverseProxyName,
                                 String reverseProxyHost,
                                 int reverseProxyPort,
                                 String reverseProxyConnectionType,
                                 String reverseProxyUsername,
                                 String reverseProxyPassword) {
        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
        this.domainName = domainName;
        this.reverseProxyName = reverseProxyName;
        this.reverseProxyHost = reverseProxyHost;
        this.reverseProxyPort = reverseProxyPort;
        this.reverseProxyConnectionType = reverseProxyConnectionType;
        this.reverseProxyUsername = reverseProxyUsername;
        this.reverseProxyPassword = reverseProxyPassword;
    }

    /**
     * Creates resource and adds to server.
     */
    public void execute() {
        try {
            login();

            String reverseProxyString = createReverseProxyString();
            addReverseProxy(reverseProxyString);

            logout();

            System.out.println("Resource created successfully!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Performs server management login.
     *
     * @throws Exception if an error occurs
     */
    private void login()
            throws Exception {
        URL url = new URL(String.format(LOGIN_URL_TEMPLATE, this.host, this.port));
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        try {
            connection.setDoOutput(true);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/json");

            String authenticationString = String.format(LOGIN_TEMPLATE, this.username, this.password);
            byte[] authenticationData = authenticationString.getBytes(CHARSET);

            connection.setRequestProperty("Content-Length", Integer.toString(authenticationData.length));

            OutputStream out = connection.getOutputStream();
            out.write(authenticationData);
            out.close();

            assertRequestSucceeded(connection);

            String cookie = connection.getHeaderField("Set-Cookie");
            this.sessionId = new Scanner(cookie).useDelimiter(";").next();
        } finally {
            connection.disconnect();
        }
    }

    /**
     * Performs server management logout.
     *
     * @throws Exception if an error occurs
     */
    private void logout()
            throws Exception {
        URL url = new URL(String.format(LOGOUT_URL_TEMPLATE, this.host, this.port));
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        try {
            connection.setDoOutput(true);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Cookie", this.sessionId);
        } finally {
            connection.disconnect();
        }
    }

    /**
     * Adds the reverse proxy to the domain.
     *
     * @param reverseProxyString the reverse proxy string
     *
     * @throws Exception if an error occurs
     */
    private void addReverseProxy(String reverseProxyString)
            throws Exception {
        URL url = new URL(String.format(ADD_PROXY_URL_TEMPLATE, this.host, this.port, this.domainName));
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        try {
            connection.setDoOutput(true);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Cookie", this.sessionId);
            connection.setRequestProperty("Content-Type", "application/json");

            byte[] proxyData = reverseProxyString.getBytes(CHARSET);

            connection.setRequestProperty("Content-Length", Integer.toString(proxyData.length));

            OutputStream out = connection.getOutputStream();
            out.write(proxyData);
            out.close();

            assertRequestSucceeded(connection);
        } finally {
            connection.disconnect();
        }
    }

    /**
     * Asserts that request was successful.
     *
     * @param connection target connection
     *
     * @throws Exception if an error occurs
     */
    private void assertRequestSucceeded(HttpURLConnection connection)
            throws Exception {
        int code = connection.getResponseCode();
        if (code < 200 || code >= 300) {
            throw new Exception(String.format("HTTP request error. Response code: %d", code));
        }
    }

    /**
     * Creates a reverse proxy string.
     *
     * @return a reverse proxy String
     */
    private String createReverseProxyString() {
        boolean passive = true;

        return String.format(FTP_REVERSE_PROXY_TEMPLATE,
                             this.reverseProxyName,
                             this.reverseProxyHost,
                             this.reverseProxyPort,
                             this.reverseProxyConnectionType,
                             this.reverseProxyUsername,
                             this.reverseProxyPassword,
                             passive);
    }

}
