package cz.muni.reddening;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;
import java.util.TimeZone;

/**
 *  Class which handles querying database
 */
@SuppressWarnings("SqlDialectInspection")
public final class SQLHandler {
    private static final Logger log = LoggerFactory.getLogger(SQLHandler.class);
    private final float x;
    private final float y;
    private final float dist;
    private final String sort;
    private final String order;
    private final int offset;
    private final int limit;

    /**
     * Constructor for class SQLHandler
     * @param x is the first coordinate in degrees
     * @param y is the second coordinate in degrees
     * @param dist is the third coordinate in parsecs
     * @param sort specifies if we sort query ascending or descending
     * @param order is the attribute by which we sort the query
     * @param offset is the number from which we start showing the records
     * @param limit is the number from which we end showing the records
     */
    SQLHandler(float x, float y, float dist, String sort,
               String order, int offset, int limit) {
        this.x = x;
        this.y = y;
        this.dist = dist;
        this.sort = sort;
        this.order = order;
        this.offset = offset;
        this.limit = limit;
    }

    /**
     *
     * @param configuration is configuration used by application
     * @return result of query in JSON format
     * @throws SQLException if SQL connection fails, or query is in wrong format
     * @throws ClassNotFoundException if class to connect to SQL url doesnt exist
     */
    public String execute(final Configuration configuration) throws SQLException, ClassNotFoundException {
        log.info("SQLHandler started working with: {} {} {} {} {} {} {} {} {} {}", x, y, dist, sort, order, offset, limit,
                configuration.getSqlUrl(), configuration.getSqlClass(), configuration.getSqlUser() );
        Class.forName(configuration.getSqlClass());
        TimeZone timeZone = TimeZone.getTimeZone("UTC"); // e.g. "Europe/Rome"
        TimeZone.setDefault(timeZone);
        log.info(configuration.getSqlUrl() + " " + configuration.getSqlUser() + " " + configuration.getSqlPassword());
        Connection con = DriverManager.getConnection(
                configuration.getSqlUrl(),
                configuration.getSqlUser(),
                configuration.getSqlPassword());
        @SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection"})
        PreparedStatement statement = con.prepareStatement("select identification,"
                + " X, Y, Distance_from_the_Sun, Reddening, "
                + "(" + (Math.pow(dist, 2))
                + "+ POWER(Distance_from_the_Sun, 2) - "
                + "2 * Distance_from_the_Sun *"
                + dist
                + " * COS (" + (x / 180 * Math.PI) + ")"
                + " * COS (X/180*PI()) * COS(Y/ 180 *PI() - "
                + (y / 180 * Math.PI) + ") -" + "2 * Distance_from_the_Sun *" + dist
                + " * SIN (" + (x / 180 * Math.PI) + ") "
                + "* SIN (X/180*PI())"
                + ")" + " as eucldist from " +  configuration.getDatabase()+ "."
                + configuration.getTable()
                + " order by " + sort + " " + order + " limit ?, ?");
        statement.setInt(1, offset);
        statement.setInt(2, limit);
        log.info(statement.toString());
        ResultSet rs = statement.executeQuery();
        StringBuilder format = new StringBuilder("[");
        boolean first = true;
        while (rs.next()) {
            if (first) {
                first = false;
            } else {
                format.append(",");
            }
            format.append(new StarRecord(rs.getString("identification"), rs.getString("X"), rs.getString("Y"),
                    rs.getString("Distance_from_the_Sun"), rs.getString("Reddening"), rs.getString("eucldist")).toJson());
        }
        format.append("]");
        Statement countStm = con.createStatement();
        @SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection"})
        ResultSet countQuery = countStm.executeQuery("select COUNT(*) as count from " +  configuration.getDatabase()
                + "." +  configuration.getTable());
        countQuery.next();
        int count = countQuery.getInt("count");
        con.close();
        log.info("success querying database {}", count);
        return "{\"total\" : " + count + ", \"rows\" : " + format.toString() + "}";
    }
}
