package database;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import objects.ResultsDTO;

public class DriverDB extends Database {

    private static DriverDB instance = new DriverDB();

    private DriverDB() {
    }

    public static DriverDB getInstance() {
        return instance;
    }

    /**
     *
     * Hämta ut kuskens id från namn
     *
     * @param name - Sträng med formatet "LastName firstName"
     * @return
     */
    public int getDriverIdByName(String name) {
        int returnValue = -1;
        final String sql = "SELECT id FROM Driver WHERE name = ?";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setString(1, name);

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                returnValue = rs.getInt("id");
            }
        } catch (final SQLException e) {
            e.printStackTrace();
        }

        return returnValue;
    }

    public int getDriverTravsportId(int driverId) {
        int returnValue = -1;
        final String sql = "SELECT travsportId FROM Driver WHERE id = ?";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setInt(1, driverId);

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                returnValue = rs.getInt("id");
            }
        } catch (final SQLException e) {
            e.printStackTrace();
        }

        return returnValue;
    }

    public ResultsDTO avarageTime(int driverId, String date) {
        return avarageTimeByDistance(driverId, 0, 0, date);
    }

    public ResultsDTO avarageTimeByDistance(int driverId, int distance, String date) {
        return avarageTimeByDistance(driverId, distance, 0, date);
    }

    public ResultsDTO avarageTimeWithLimit(int driverId, int raceLimit, String date) {
        return avarageTimeByDistance(driverId, 0, raceLimit, date);
    }

    public ResultsDTO avarageTimeByDistance(int driverId, int distance, int raceLimit, String date) {
        final ResultsDTO returnValue = new ResultsDTO();
        final String sql
                = "SELECT ROUND(AVG(avgRes.Time), 2) as avgTime, count(*) as num FROM (SELECT Time FROM Results WHERE Time > 0 AND Lane > 0 AND RaceDate < NOW() AND DriverId = ?";
        final String distanceSql = " AND distance BETWEEN ? AND ?";
        final String limitSql = " LIMIT ?";
        final String endingSql = ") avgRes";
        final String orderSql = " ORDER BY RaceDate DESC";

        final StringBuilder sb = new StringBuilder();
        sb.append(sql);

        boolean orderAppended = false;
        if (distance > 0) {
            sb.append(distanceSql);
            sb.append(orderSql);
            orderAppended = true;
        }
        if (raceLimit > 0) {
            if (!orderAppended) {
                sb.append(orderSql);
                orderAppended = true;
            }
            sb.append(limitSql);
        }

        if (!orderAppended) {
            sb.append(orderSql);
        }
        sb.append(endingSql);

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sb.toString());
            stat.setInt(1, driverId);
            if (distance > 0) {
                stat.setInt(2, distance - 50);
                stat.setInt(3, distance + 50);
            }
            if (distance > 0 && raceLimit > 0) {
                stat.setInt(4, raceLimit);
            } else if (distance <= 0 && raceLimit > 0) {
                stat.setInt(2, raceLimit);
            }

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                returnValue.setRes(rs.getFloat("avgTime"));
                returnValue.setCount(rs.getInt("num"));
            }
        } catch (final SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

        return returnValue;
    }

    public ResultsDTO avaragePlacement(int driverId, String date) {
        return avaragePlacementByDistance(driverId, 0, 0, date);
    }

    public ResultsDTO avaragePlacementByDistance(int driverId, int distance, String date) {
        return avaragePlacementByDistance(driverId, distance, 0, date);
    }

    public ResultsDTO avaragePlacementWithLimit(int driverId, int raceLimit, String date) {
        return avaragePlacementByDistance(driverId, 0, raceLimit, date);
    }

    public ResultsDTO avaragePlacementByDistance(int driverId, int distance, int raceLimit, String date) {
        final ResultsDTO returnValue = new ResultsDTO();

        final StringBuilder sb = new StringBuilder();
        final String sql = "SELECT ROUND(avg(" + "CASE " + "WHEN Result = -1 THEN 9 " + "WHEN Result = -2 THEN 10 "
                + "WHEN Result = 0 THEN 8 " + "ELSE Result " + "END "
                + "), 2) as avgResult, count(*) as num FROM (SELECT Result FROM Results WHERE DriverId = ?";
        final String distSql = " AND distance BETWEEN ? AND ?";
        final String limitSql = " limit ?";

        sb.append(sql);
        if (distance > 0) {
            sb.append(distSql);
        }
        if (raceLimit > 0) {
            sb.append(limitSql);
        }

        sb.append(") t");

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sb.toString());
            stat.setInt(1, driverId);
            if (distance > 0) {
                stat.setInt(2, distance - 50);
                stat.setInt(3, distance + 50);
            }
            if (distance > 0 && raceLimit > 0) {
                stat.setInt(4, raceLimit);
            } else if (distance <= 0 && raceLimit > 0) {
                stat.setInt(2, raceLimit);
            }

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                returnValue.setRes(rs.getFloat("avgResult"));
                returnValue.setCount(rs.getInt("num"));
            }
        } catch (final SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return returnValue;
    }
}
