package com.thinkaurelius.faunus.formats.rexster;

import com.thinkaurelius.faunus.formats.rexster.util.HttpHelper;
import org.apache.commons.codec.binary.Base64;
import org.apache.hadoop.conf.Configuration;
import org.codehaus.jettison.json.JSONObject;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;

/**
 * @author Stephen Mallette (http://stephen.genoprime.com)
 */
public class RexsterConfiguration {

    public static final int VERTEX_TRUE_COUNT = -1;

    public static final String FAUNUS_GRAPH_INPUT_REXSTER_HOSTNAME = "faunus.graph.input.rexster.hostname";
    public static final String FAUNUS_GRAPH_INPUT_REXSTER_PORT = "faunus.graph.input.rexster.port";
    public static final String FAUNUS_GRAPH_INPUT_REXSTER_SSL = "faunus.graph.input.rexster.ssl";
    public static final String FAUNUS_GRAPH_INPUT_REXSTER_GRAPH = "faunus.graph.input.rexster.graph";
    public static final String FAUNUS_GRAPH_INPUT_REXSTER_V_ESTIMATE = "faunus.graph.input.rexster.v-estimate";
    public static final String FAUNUS_GRAPH_INPUT_REXSTER_USERNAME = "faunus.graph.input.rexster.username";
    public static final String FAUNUS_GRAPH_INPUT_REXSTER_PASSWORD = "faunus.graph.input.rexster.password";

    private Configuration conf;

    private static long trueVertexCount = Long.MIN_VALUE;

    public RexsterConfiguration(final Configuration job) {
        this.conf = job;
    }

    public Configuration getConf() {
        return conf;
    }

    public boolean getSsl() {
        return this.conf.getBoolean(FAUNUS_GRAPH_INPUT_REXSTER_SSL, false);
    }

    public String getRestAddress() {
        return this.conf.get(FAUNUS_GRAPH_INPUT_REXSTER_HOSTNAME);
    }

    public int getRestPort() {
        return this.conf.getInt(FAUNUS_GRAPH_INPUT_REXSTER_PORT, 8182);
    }

    public String getGraph() {
        return this.conf.get(FAUNUS_GRAPH_INPUT_REXSTER_GRAPH);
    }

    public String getAuthenticationHeaderValue() {
        return "Basic " + Base64.encodeBase64URLSafeString(
                (this.conf.get(FAUNUS_GRAPH_INPUT_REXSTER_USERNAME, "") + ":" + this.conf.get(FAUNUS_GRAPH_INPUT_REXSTER_USERNAME, ""))
                        .getBytes());
    }

    public synchronized long getEstimatedVertexCount() {
        long vertexCount = this.conf.getInt(FAUNUS_GRAPH_INPUT_REXSTER_V_ESTIMATE, 10000);

        // getting the true count means doing a g.V over in rexster.  this method is synchronized so that
        // g.V.count() is not called more than once for the job.
        if (vertexCount == VERTEX_TRUE_COUNT) {
            if (trueVertexCount == Long.MIN_VALUE) {
                trueVertexCount = getTrueVertexCount();
            }

            vertexCount = trueVertexCount;
        }

        return vertexCount;
    }

    public String getHttpProtocol() {
        return this.getSsl() ? "https" : "http";
    }

    public String getRestEndpoint() {
        return String.format("%s://%s:%s/graphs/%s/%s/%s",
                this.getHttpProtocol(), this.getRestAddress(),
                this.getRestPort(), this.getGraph(), FaunusRexsterInputFormatExtension.EXTENSION_NAMESPACE,
                FaunusRexsterInputFormatExtension.EXTENSION_NAME);
    }

    public String getRestStreamEndpoint() {
        return String.format("%s/%s",
                this.getRestEndpoint(), FaunusRexsterInputFormatExtension.EXTENSION_METHOD_STREAM);
    }

    public String getRestCountEndpoint() {
        return String.format("%s/%s",
                this.getRestEndpoint(), FaunusRexsterInputFormatExtension.EXTENSION_METHOD_COUNT);
    }

    private long getTrueVertexCount() {
        try {
            final HttpURLConnection connection = HttpHelper.createConnection(
                    this.getRestCountEndpoint(), this.getAuthenticationHeaderValue());
            final JSONObject json = new JSONObject(convertStreamToString(connection.getInputStream()));

            return json.optLong(FaunusRexsterInputFormatExtension.EXTENSION_METHOD_COUNT);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    private static String convertStreamToString(final InputStream is) throws Exception {
        final BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        final StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line + "\n");
        }
        is.close();
        return sb.toString();
    }
}
