package controllers;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.text.ParseException;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;

import objects.DaysSinceLastRace;
import objects.HorseRaceData;
import objects.LaneWinObject;
import objects.Result;
import objects.UpdateRaceRow;
import parsers.TravsportParser;

public class DatabaseController {

    private static final DatabaseController instance = new DatabaseController();

    private static final String username = "atg";
    private static final String password = "CvWKY34DqtlVgjt_9";
    private static final String database = "atg";
    private static final String url = "jdbc:mysql://nordh.xyz:3306/";
    private static final String timezoneFix
            = "?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";

    private Connection conn;

    protected DatabaseController() {
        getConnection();
    }

    public static DatabaseController getInstance() {
        return instance;
    }

    protected Connection getConnection() {
        if (conn == null) {
            try {
                conn = DriverManager.getConnection(url + database + timezoneFix, username, password);
            } catch (final SQLException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        return conn;
    }

    public void insertResult(String trackShortCode, String date, int raceNumber, int lane, int distance, int result, float time,
            String timeModifier, int shoes, int sulky, String driver, String trainer, String horseName, int travsportIdHorse,
            int travsportIdTrainer, int travsportIdDriver, String raceType, int raceId) {
        final String sql;
        final boolean raceToday = date.equals(MainController.DATE_FORMAT.format(new Date()));
        horseName = horseName.replaceAll("[^a-zåäöA-ZÅÄÖ0-9 ()\\.']", "");
        driver = driver.replaceAll("[^a-zåäöA-ZÅÄÖ0-9] ()\\.'", "");
        if (raceToday) {
            sql = "INSERT INTO "
                    + "`Results`(`TrackId`, `RaceDate`, `RaceNumber`, `Lane`, `Distance`, `Result`, `Time`, `TimeModifier`, "
                    + "`Shoes`, `Sulky`, `DriverId`, `TrainerId`, `HorseId`, raceId, RaceType) "
                    + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE id=id, Result = ?, Time = ?, RaceType = CONCAT(RaceType, ?), RaceId = ?";
        } else {
            sql = "INSERT INTO "
                    + "`Results`(`TrackId`, `RaceDate`, `RaceNumber`, `Lane`, `Distance`, `Result`, `Time`, `TimeModifier`, "
                    + "`Shoes`, `Sulky`, `DriverId`, `TrainerId`, `HorseId`, raceId) "
                    + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE id=id, Result = ?, Time = ?, RaceId = ?";
        }

        final int horseId = getHorseId(horseName, travsportIdHorse);
        final int driverId = getDriverId(driver, travsportIdDriver);
        final int trainerId = getTrainerId(trainer, travsportIdTrainer);
        final int trackId = getTrackIdFromShortcode(trackShortCode);

        if (horseId == -1 || driverId == -1) {
            return;
        }
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);

            stat.setInt(1, trackId);
            stat.setString(2, date);
            stat.setInt(3, raceNumber);
            stat.setInt(4, lane);
            stat.setInt(5, distance);
            stat.setInt(6, result);
            stat.setFloat(7, time);
            stat.setString(8, timeModifier);
            stat.setInt(9, shoes);
            stat.setInt(10, sulky);
            stat.setInt(11, driverId);
            if (trainerId < 0) {
                stat.setNull(12, Types.INTEGER);
            } else {
                stat.setInt(12, trainerId);
            }
            stat.setInt(13, horseId);
            stat.setInt(14, result < 0 ? raceId : -1);

            if (raceToday) {
                stat.setString(15, raceType);

                stat.setInt(16, result);
                stat.setFloat(17, time);
                stat.setString(18, ", " + raceType);
                stat.setInt(19, raceId);
            } else {
                stat.setInt(15, result);
                stat.setFloat(16, time);
                stat.setInt(17, raceId);
            }
            stat.executeUpdate();

        } catch (final SQLException e) {
            System.out.println("Failing sql " + sql + "[ " + trackId + ", " + date + ", " + raceNumber + ", " + lane + ", "
                    + distance + ", " + result + ", " + time + ", " + timeModifier + ", " + shoes + "," + sulky + ", " + driverId
                    + ", " + trainerId + ", " + horseId + ", " + raceId + "]");
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    private int insertNewName(String table, String name, int travsportId) { // travsportId inte generell, behövs en
        // bättre lösning
        final String sql = "INSERT INTO " + table + " (name, travsportId) VALUES (?, ?)";
        int last_id = -1;
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
            stat.setString(1, name);
            stat.setInt(2, travsportId);

            stat.executeUpdate();
            final ResultSet generatedId = stat.getGeneratedKeys();
            while (generatedId.next()) {
                last_id = generatedId.getInt(1);
            }

        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return last_id;
    }

    private int getTrackIdFromShortcode(String shortcode) {
        final String trackSql = "SELECT id FROM Track WHERE shortCode = ?";
        int trackId = -1;
        try {
            final PreparedStatement stat = getConnection().prepareStatement(trackSql);
            stat.setString(1, shortcode);

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                trackId = rs.getInt("id");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return trackId;
    }

    public int getHorseId(String horseName, int travsportId) {
        horseName = horseName.replaceAll("[^a-zåäöA-ZÅÄÖ0-9 ()\\.']", "");
        final String horseSql = "SELECT id FROM Horse WHERE name = ? OR travsportId = ?";
        int horseId = -1;
        try {
            final PreparedStatement stat = getConnection().prepareStatement(horseSql);
            stat.setString(1, horseName);
            stat.setInt(2, travsportId);

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                horseId = rs.getInt("id");
            }
            if (horseId == -1 && !Strings.isNullOrEmpty(horseName)) {
                horseId = insertNewName("Horse", horseName, travsportId);
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return horseId;
    }

    public int getHorseTravsportId(String horseName) {
        final String sql = "SELECT travsportId FROM Horse WHERE name = ?";
        int id = -1;

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

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                id = rs.getInt("travsportId");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return id;
    }

    public int getDriverId(String driverName, int travsportId) {
        driverName = driverName.replaceAll("[^a-zA-Z0-9 ]", "");
        final String driverSql = "SELECT id FROM Driver WHERE name = ? OR travsportId = ?";
        int driverId = -1;
        try {
            final PreparedStatement stat = getConnection().prepareStatement(driverSql);
            stat.setString(1, driverName);
            stat.setInt(2, travsportId);

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                driverId = rs.getInt("id");
            }
            if (driverId == -1 && !Strings.isNullOrEmpty(driverName)) {
                driverId = insertNewName("Driver", driverName, travsportId);
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return driverId;
    }

    private int getTrainerId(String trainerName, int travsportId) {
        trainerName = trainerName.replaceAll("[^a-zA-Z0-9 ]", "");
        final String trainerSql = "SELECT id FROM Trainer WHERE name = ? OR travsportId = ?";
        int trainerId = -1;
        try {
            final PreparedStatement stat = getConnection().prepareStatement(trainerSql);
            stat.setString(1, trainerName);
            stat.setInt(2, travsportId);

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                trainerId = rs.getInt("id");
            }
            if (trainerId == -1 && !Strings.isNullOrEmpty(trainerName)) {
                trainerId = insertNewName("Trainer", trainerName, travsportId);
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return trainerId;
    }

    public void setHorseTravsportId(int horseId, String horseName) {
        final String sql = "UPDATE Horse SET travsportId = ? WHERE name = ?";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setInt(1, horseId);
            stat.setString(2, horseName);

            stat.executeUpdate();
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    // Lane stats SELECT Lane, Count(*) FROM `Results` WHERE Result = 1 AND
    // TimeModifier NOT like "%a%" GROUP BY Lane ORDER BY `Results`.`Lane` ASC
    // SELECT Lane, (Count(*) / SUM(COUNT(*)) OVER()) * 100 AS Percent, Distance
    // FROM `Results` WHERE Result = 1 AND TimeModifier NOT like '%a%' AND Lane > 0
    // AND Distance IN (2140,2160) GROUP BY Lane, Distance ORDER BY `Results`.`Lane`
    // ASC

    public List<LaneWinObject> getAutostartWinPercents(ArrayList<String> distance) {
        final List<LaneWinObject> resultList = Lists.newArrayList();
        final String sql
                = "SELECT Lane, (Count(*) / SUM(COUNT(*)) OVER()) * 100 AS Percent, distance FROM `Results` WHERE Result = 1 AND TimeModifier like '%a%' AND Lane > 0 AND Distance IN (?) GROUP BY Lane, Distance ORDER BY `Results`.`Lane` ASC";
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setString(1, String.join(",", distance));
            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                resultList.add(new LaneWinObject(rs.getInt("Lane"), rs.getInt("distance"), rs.getFloat("Percent")));
            }

        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return resultList;
    }

    public List<LaneWinObject> getAutostartWinPercents(List<Integer> distances) {
        return getAutostartWinPercents(
                Lists.newArrayList(distances.stream().map(d -> d.toString()).collect(Collectors.toList())));
    }

    public List<LaneWinObject> getVoltstartWinPercents(ArrayList<String> distance) {
        final List<LaneWinObject> resultList = Lists.newArrayList();
        String sql
                = "SELECT Lane, (Count(*) / SUM(COUNT(*)) OVER()) * 100 AS Percent, distance FROM `Results` WHERE Result = 1 AND TimeModifier NOT like '%a%' AND Lane > 0 AND Distance IN (?) GROUP BY Lane, Distance ORDER BY `Results`.`Lane` ASC";
        try {
            final String in = distance.stream().collect(Collectors.joining(",", "(", ")"));
            sql = sql.replace("(?)", in);
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                resultList.add(new LaneWinObject(rs.getInt("Lane"), rs.getInt("distance"), rs.getFloat("Percent")));
            }

        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return resultList;
    }

    public List<LaneWinObject> getVoltstartWinPercents(List<Integer> distance) {
        return getVoltstartWinPercents(Lists.newArrayList(distance.stream().map(m -> m.toString()).collect(Collectors.toList())));
    }

    public List<HorseRaceData> getRacesFromDate(final String date, final boolean shouldUpdate) {
        final List<HorseRaceData> results = new ArrayList<>();

        final String sql = "SELECT r1.*, driver.name as DriverName, horse.name as HorseName, track.name as TrackName "
                + "FROM Results r1 " + "INNER JOIN Driver as driver ON driverId = driver.id "
                + "INNER JOIN Horse as horse ON HorseId = horse.id " + "INNER JOIN Track as track ON TrackId = track.id "
                + "WHERE r1.RaceDate = ? " + " GROUP BY r1.horseId "
                + "ORDER BY r1.TrackId DESC, r1.RaceDate DESC, `r1`.`RaceNumber` ASC";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);

            stat.setString(1, date);

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                final HorseRaceData rd = new HorseRaceData();
                rd.setDistance(rs.getInt("Distance"));
                rd.setDriverId(rs.getInt("DriverId"));
                rd.setDriverName(rs.getString("DriverName"));
                rd.setHorseName(rs.getString("HorseName"));
                rd.setHorseId(rs.getInt("HorseId"));
                rd.setTrainerId(rs.getInt("TrainerId"));
                rd.setRaceNumber(rs.getInt("RaceNumber"));
                rd.setTrackName(rs.getString("TrackName"));
                rd.setRaceDate(rs.getString("RaceDate"));
                rd.setLane(rs.getInt("Lane"));
                rd.setResult(rs.getInt("Result"));
                rd.setTime(rs.getFloat("Time"));
                rd.setTimeModifier(rs.getString("TimeModifier"));
                rd.setShoes(rs.getInt("Shoes"));
                rd.setSulky(rs.getInt("Sulky"));

                if (shouldUpdate) {
                    if (rd.getAvgDriverTimeCount() < 20) {
                        System.out.println("Uppdating driver " + rd.getDriverName() + "(" + rd.getDriverId() + ") travsport id: "
                                + getDriverTravsportId(rd.getDriverName()) + "because race count was "
                                + rd.getAvgDriverTimeCount());
                        TravsportParser.getInstance().getDriverStatById(getDriverTravsportId(rd.getDriverName()),
                                rd.getDriverName(),
                                rs.getInt("raceId"));
                    }

                    if (rd.getAvgHorseTimeCount() < 5) {
                        System.out.println("Uppdating horse " + rd.getHorseName() + "( " + rd.getHorseId() + ") travsport id: "
                                + getHorseTravsportId(rd.getHorseName()) + " because race count was "
                                + rd.getAvgHorseTimeCount());
                        TravsportParser.getInstance().getHorseStatByIdJson(getHorseTravsportId(rd.getHorseName()),
                                rd.getHorseName(),
                                "",
                                rs.getInt("raceId"));
                    }
                }

                results.add(rd);
            }
        } catch (final SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return results;
    }

    public List<HorseRaceData> getTodaysRaces(String type, String date, boolean shouldUpdate) {
        final String sql;
        Date date2 = new Date();
        try {
            date2 = MainController.DATE_FORMAT.parse(date);
        } catch (final ParseException e1) {
            throw new RuntimeException(e1.getMessage(), e1);
        }
        final Calendar c = Calendar.getInstance();
        c.setTime(date2);
        c.add(Calendar.YEAR, -2);

        if (Strings.isNullOrEmpty(type)) {
            sql = "SELECT r1.*, driver.name as DriverName, horse.name as HorseName, track.name as TrackName " + "FROM Results r1 "
                    + "				INNER JOIN Driver as driver ON driverId = driver.id "
                    + "				INNER JOIN Horse as horse ON HorseId = horse.id "
                    + "				INNER JOIN Track as track ON TrackId = track.id " + " WHERE r1.RaceDate = ? "
                    + " GROUP BY r1.horseId " + "ORDER BY r1.TrackId DESC, r1.RaceDate DESC, `r1`.`RaceNumber` ASC";
        } else {
            sql = "SELECT r1.*, driver.name as DriverName, horse.name as HorseName, track.name as TrackName " + "FROM Results r1 "
                    + "				INNER JOIN Driver as driver ON driverId = driver.id "
                    + "				INNER JOIN Horse as horse ON HorseId = horse.id "
                    + "				INNER JOIN Track as track ON TrackId = track.id " + " WHERE r1.RaceDate = ? "
                    + " AND r1.RaceType LIKE ? " + " GROUP BY r1.horseId " + "ORDER BY r1.TrackId DESC, `r1`.`RaceNumber` ASC";
        }

        final List<HorseRaceData> results = new ArrayList<>();
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            if (Strings.isNullOrEmpty(type)) {
                stat.setString(1, date);
            } else {
                stat.setString(1, date);
                stat.setString(2, "%" + type + "%");
            }

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                final HorseRaceData rd = new HorseRaceData();
                rd.setDistance(rs.getInt("Distance"));
                rd.setDriverId(rs.getInt("DriverId"));
                rd.setDriverName(rs.getString("DriverName"));
                rd.setHorseName(rs.getString("HorseName"));
                rd.setHorseId(rs.getInt("HorseId"));
                rd.setTrainerId(rs.getInt("TrainerId"));
                rd.setRaceNumber(rs.getInt("RaceNumber"));
                rd.setTrackName(rs.getString("TrackName"));
                rd.setRaceDate(rs.getString("RaceDate"));
                rd.setLane(rs.getInt("Lane"));
                rd.setResult(rs.getInt("Result"));
                rd.setTime(rs.getFloat("Time"));
                rd.setTimeModifier(rs.getString("TimeModifier"));
                rd.setShoes(rs.getInt("Shoes"));
                rd.setSulky(rs.getInt("Sulky"));

                rd.setAvgHorseTime(getAvgTime(rd.getHorseId(), rd.getRaceDate(), -1, false),
                        getHorseRaceCount(rd.getHorseId(), 0));
                rd.setAvgDriverTime(getAvgTime(rd.getDriverId(), rd.getRaceDate(), -1, true),
                        getDriverRaceCount(rd.getDriverId(), 0));
                rd.setAvgHorseTimeByDistance(getAvgTime(rd.getHorseId(), rd.getRaceDate(), rd.getDistance(), false),
                        getHorseRaceCount(rd.getHorseId(), rd.getDistance()));
                rd.setAvgDriverTimeByDistance(getAvgTime(rd.getDriverId(), rd.getRaceDate(), rd.getDistance(), true),
                        getDriverRaceCount(rd.getDriverId(), rd.getDistance()));

                if (shouldUpdate) {
                    if (rd.getAvgDriverTimeCount() < 20) {
                        System.out.println("Uppdating driver " + rd.getDriverName() + "(" + rd.getDriverId() + ") travsport id: "
                                + getDriverTravsportId(rd.getDriverName()) + "because race count was "
                                + rd.getAvgDriverTimeCount());
                        TravsportParser.getInstance().getDriverStatById(getDriverTravsportId(rd.getDriverName()),
                                rd.getDriverName(),
                                rs.getInt("raceId"));
                    }

                    if (rd.getAvgHorseTimeCount() < 5) {
                        System.out.println("Uppdating horse " + rd.getHorseName() + "( " + rd.getHorseId() + ") travsport id: "
                                + getHorseTravsportId(rd.getHorseName()) + " because race count was "
                                + rd.getAvgHorseTimeCount());
                        TravsportParser.getInstance().getHorseStatByIdJson(getHorseTravsportId(rd.getHorseName()),
                                rd.getHorseName(),
                                "",
                                rs.getInt("raceId"));
                    }
                }

                results.add(rd);
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return results;
    }

    private float getAvgTime(int id, String date, int distance, boolean isDriver) {

        float returnValue = 0f;
        final String who = isDriver ? "DriverId" : "HorseId";
        String sql = "SELECT AVG(time) as avg FROM Results WHERE " + who + " = ? AND RaceDate < ? AND RaceDate < DATE(NOW())";

        if (distance > 0) {
            sql += " AND Distance BETWEEN ? AND ?";
        }

        sql += " AND time > 0 ORDER BY RaceDate DESC";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);

            stat.setInt(1, id);
            stat.setString(2, date);
            if (distance > 0) {
                stat.setInt(3, distance - 50);
                stat.setInt(4, distance + 50);
            }

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                returnValue = rs.getFloat("avg");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return returnValue;
    }

    public float getAvarageTimeForTrack(int trackId, int distance, int id, boolean isDriver) {
        final float returnValue = 0f;
        String sql = "SELECT AVG(time) as avgTime FROM Results WHERE TrackId = ?";

        if (isDriver) {
            sql += " AND DriverId = ?";
        } else {
            sql += " AND HorseId = ?";
        }

        if (distance > 0) {
            sql += " AND Distance BETWEEN ? AND ?";
        }

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setInt(1, trackId);
            stat.setInt(2, id);
            if (distance > 0) {
                stat.setInt(3, distance - 50);
                stat.setInt(4, distance + 50);
            }

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                rs.getFloat("avgTime");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return returnValue;
    }

    private int getHorseRaceCount(int horseId, int distance) {
        int returnValue = 0;
        final String sql;
        if (distance > 0) {
            sql = "SELECT Count(*) as c FROM Results WHERE HorseId = ? AND Distance = ? AND Time > 0";
        } else {
            sql = "SELECT Count(*) as c FROM Results WHERE HorseId = ? AND Time > 0";
        }
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            if (distance > 0) {
                stat.setInt(1, horseId);
                stat.setInt(2, distance);
            } else {
                stat.setInt(1, horseId);
            }
            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                returnValue = rs.getInt("c");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return returnValue;
    }

    private int getDriverRaceCount(int driverId, int distance) {
        int returnValue = 0;
        final String sql;
        if (distance > 0) {
            sql = "SELECT Count(*) as c FROM Results WHERE DriverId = ? AND Distance = ? AND Time > 0";
        } else {
            sql = "SELECT Count(*) as c FROM Results WHERE DriverId = ? AND Time > 0";
        }
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            if (distance > 0) {
                stat.setInt(1, driverId);
                stat.setInt(2, distance);
            } else {
                stat.setInt(1, driverId);
            }
            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                returnValue = rs.getInt("c");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return returnValue;
    }

    // Hämta statistik för häst, förare, kombinationen av dessa två, distans....
    public SimpleEntry<Integer, Integer> getNumHorseGallop(int horseId) {
        final String sql
                = "SELECT COUNT(*) as total, SUM(IF(TimeModifier LIKE '%g%', 1, 0)) as gallop FROM `Results` WHERE HorseId = ?";
        SimpleEntry<Integer, Integer> returnValue = null;

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

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                returnValue = new SimpleEntry<>(rs.getInt("total"), rs.getInt("gallop"));
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return returnValue;
    }

    // Hämta statistik för häst, förare, kombinationen av dessa två, distans....
    public SimpleEntry<Integer, Integer> getNumDriverGallop(int driverId) {
        final String sql
                = "SELECT COUNT(*) as total, SUM(IF(TimeModifier LIKE '%g%', 1, 0)) as gallop FROM `Results` WHERE DriverId = ?";
        SimpleEntry<Integer, Integer> returnValue = null;

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

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                returnValue = new SimpleEntry<>(rs.getInt("total"), rs.getInt("gallop"));
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return returnValue;
    }

    public int getDriverTravsportId(String driverFirstName, String driverLastName) {
        int returnValue = -1;
        final String sql = "SELECT travsportId FROM Driver WHERE name = ?";
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setString(1, driverLastName + " " + driverFirstName);

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                returnValue = rs.getInt("travsportId");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return returnValue;
    }

    public int getDriverTravsportId(String driverName) {
        int returnValue = -1;
        final String sql = "SELECT travsportId FROM Driver WHERE name = ?";
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setString(1, driverName);

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                returnValue = rs.getInt("travsportId");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return returnValue;
    }

    public void setDriverTravsportId(int driverId, String firstName, String lastName) {
        final String sql = "UPDATE Driver SET travsportId = ? WHERE name = ?";
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setInt(1, driverId);
            stat.setString(2, lastName + " " + firstName);

            stat.executeUpdate();
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    public float getTrendLast5(HorseRaceData horseRaceData) {
        final String combinedTrendSql
                = "SELECT Time, (SELECT AVG(Time) FROM Results WHERE HorseId = ? AND DriverId = ? AND Distance = ?) as avgTime FROM Results WHERE HorseId = ? AND DriverId = ? AND Distance = ? AND Time > -1 ORDER BY RaceDate ASC limit 5";
        final String DriverTrendSql
                = "SELECT Time, (SELECT AVG(Time) FROM Results WHERE DriverId = ? AND Distance = ?) as avgTime FROM Results WHERE DriverId = ? AND Distance = ? AND Time > -1 ORDER BY RaceDate ASC limit 5";
        final String horseTrendSql
                = "SELECT Time, (SELECT AVG(Time) FROM Results WHERE HorseId = ? AND Distance = ?) as avgTime FROM Results WHERE HorseId = ? AND Distance = ? AND Time > -1  ORDER BY RaceDate ASC limit 5";

        final int horseId = horseRaceData.getHorseId();
        final int driverId = horseRaceData.getDriverId();
        final int distance = horseRaceData.getDistance();

        float avgHorseTime = -1f;
        float avgDriverTime = -1f;
        float avgCombinedTime = -1f;
        float startHorseAvgTime = -1;
        float startDriverAvgTime = -1;
        float startCombinedAvgTime = -1;
        try {
            final PreparedStatement horseStat = getConnection()
                    .prepareStatement(horseTrendSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            horseStat.setInt(1, horseId);
            horseStat.setInt(2, distance);
            horseStat.setInt(3, horseId);
            horseStat.setInt(4, distance);

            final ResultSet horseRs = horseStat.executeQuery();
            if (horseRs.next()) {
                startHorseAvgTime = horseRs.getFloat("avgTime");
                ;
                horseRs.beforeFirst();
                avgHorseTime = getTrendAvgTime(horseRs);
                // This should be the return value for horse
                System.out.println("Horse trend is " + (avgHorseTime - startHorseAvgTime));
            } else {
                System.out.println("No Horse trend found");
            }

            horseRs.close();
            horseStat.close();

            final PreparedStatement driverStat
                    = conn.prepareStatement(DriverTrendSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            driverStat.setInt(1, driverId);
            driverStat.setInt(2, distance);
            driverStat.setInt(3, driverId);
            driverStat.setInt(4, distance);
            final ResultSet driverRs = driverStat.executeQuery();

            if (driverRs.next()) {
                startDriverAvgTime = driverRs.getFloat("avgTime");
                ;
                driverRs.beforeFirst();
                avgDriverTime = getTrendAvgTime(driverRs);
                System.out.println("Driver trend is " + (avgDriverTime - startDriverAvgTime));
            } else {
                System.out.println("No driver trend found");
            }

            final PreparedStatement combinedStat
                    = conn.prepareStatement(combinedTrendSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

            combinedStat.setInt(1, horseId);
            combinedStat.setInt(2, driverId);
            combinedStat.setInt(3, distance);
            combinedStat.setInt(4, horseId);
            combinedStat.setInt(5, driverId);
            combinedStat.setInt(6, distance);
            final ResultSet combinedRs = combinedStat.executeQuery();

            if (combinedRs.next()) {
                startCombinedAvgTime = combinedRs.getFloat("avgTime");
                ;
                combinedRs.beforeFirst();
                avgCombinedTime = getTrendAvgTime(combinedRs);
                System.out.println("Combined trend is " + (avgCombinedTime - startCombinedAvgTime));
            } else {
                System.out.println("No combined trend available");
            }

            if (avgHorseTime > -1 && avgDriverTime > -1) {
                System.out.println("Combination of horse and driver: "
                        + ((avgHorseTime - startHorseAvgTime) + (avgDriverTime - startDriverAvgTime)));
            }
            System.out.println("------");

        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return ((avgHorseTime - startHorseAvgTime) + (avgDriverTime - startDriverAvgTime)) / 2f;
    }

    private float getTrendAvgTime(final ResultSet rs) throws SQLException {
        float returnValue = 0.0f;
        while (rs.next()) {
            if (returnValue == 0.0f) {
                returnValue = rs.getFloat("avgTime");
            }

            if (returnValue == 0f) {
                returnValue = rs.getFloat("Time");
            }
            final float diff = -(returnValue - rs.getFloat("Time"));
            returnValue = returnValue + diff;
        }
        return returnValue;
    }

    public void updateResult(String horseName, String driverName, int result, String timeModifier, float timeValue, String date,
            String type) {
        final String sql = "UPDATE Results " + "SET " + "Result = ?, Time = ?, TimeModifier = ?" + "WHERE "
                + "HorseId = (SELECT id FROM Horse WHERE name = ?) AND " + "DriverId = (SELECT id FROM Driver WHERE name = ?) AND"
                + "RaceDate = ?";
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setInt(1, result);
            stat.setFloat(2, timeValue);
            stat.setString(3, timeModifier);
            stat.setString(4, horseName);
            stat.setString(5, driverName);
            stat.setString(6, date);

            final int res = stat.executeUpdate();

            if (res != 1) {
                System.out.println(
                        "Updated " + res + " results for horse " + horseName + " and driver " + driverName + " at date " + date);
            }

        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

    }

    public String getTrackShortCodeByName(String trackName) {
        final String sql = "SELECT shortcode FROM Track WHERE name = ?";
        String trackcode = null;
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setString(1, trackName);

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                trackcode = rs.getString("shortcode");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return trackcode;
    }

    public int getRaceCount(String what, int id) {
        int returnValue = 0;
        final String sql;
        if (what.equals("Horse")) {
            sql = "SELECT Count(*) as count FROM Results WHERE HorseId = ?";
        } else if (what.equals("Driver")) {
            sql = "SELECT Count(*) as count FROM Results WHERE DriverId = ?";
        } else {
            sql = "";
        }

        if (!Strings.isNullOrEmpty(sql)) {
            try {
                final PreparedStatement stat = getConnection().prepareStatement(sql);
                stat.setInt(1, id);

                final ResultSet rs = stat.executeQuery();
                while (rs.next()) {
                    returnValue = rs.getInt("count");
                }

            } catch (final SQLException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        return returnValue;
    }

    public int getRaceId() {
        final String sql
                = "SELECT raceId FROM Results where raceId > -1 AND RaceDate < DATE(NOW()) ORDER BY RaceDate ASC LIMIT 1";
        int returnValue = -1;
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                returnValue = rs.getInt("raceId");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return returnValue;

    }

    public List<UpdateRaceRow> getRacesToUpdate(String date) {
        final List<UpdateRaceRow> racesToUpdate = Lists.newArrayList();
        final String sql
                = "SELECT r.*, t.name as TrackName, t.id as TrackId FROM Results r INNER JOIN Track t ON r.trackId = t.id WHERE r.RaceDate = ? GROUP BY r.RaceDate, t.name, r.RaceNumber";

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

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                racesToUpdate.add(new UpdateRaceRow(rs.getInt("id"), rs.getString("RaceDate"), rs.getString("RaceType"),
                        rs.getString("TimeModifier").contains("a"), rs.getInt("RaceNumber"), rs.getString("TrackName"),
                        rs.getInt("TrackId")));
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return racesToUpdate;

    }

    public void updateRaceInfo(int raceId, boolean isAutostart, String raceType, int raceNumber, String date, int trackId) {
        final String sql = "UPDATE Results SET RaceType = ? WHERE raceId = ? AND RaceNumber = ? AND trackId = ? AND RaceDate = ?";
        final String updateTimeModifier;
        if (isAutostart) {
            updateTimeModifier = "UPDATE `Results` SET TimeModifier = " + "IF(TimeModifier LIKE '%a%', " + "TimeModifier, "
                    + "CONCAT(TimeModifier, 'a')) " + "WHERE RaceDate = ? AND RaceNumber = ? AND TrackId = ?";
        } else {
            updateTimeModifier = "UPDATE `Results` SET TimeModifier = " + "REPLACE(TimeModifier, 'a', '') "
                    + "WHERE RaceDate = ? AND RaceNumber = ? AND TrackId = ?";
        }

        try {
            PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setString(1, raceType);
            stat.setInt(2, raceId);
            stat.setInt(3, raceNumber);
            stat.setInt(4, trackId);
            stat.setString(5, date);

            stat.executeUpdate();

            stat = getConnection().prepareStatement(updateTimeModifier);
            stat.setString(1, date);
            stat.setInt(2, raceNumber);
            stat.setInt(3, trackId);

            stat.executeUpdate();

        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

    }

    public void updateFromRaceResults(int raceId, int result, float time, String timeModifier, int lane, int distance,
            int horseTravsportId, int driverTravsportId, String raceDate, int raceNumber) {
        final int horseId = getHorseId("", horseTravsportId);
        final int driverId = getDriverId("", driverTravsportId);
        final String sql
                = "UPDATE Results SET Result = ?, Time = ?, TimeModifier = ?, DriverId = ?, raceId = -1 WHERE RaceDate = ? AND RaceId = ? AND RaceNumber = ? AND HorseId = ?";
        if (horseId == -1 || driverId == -1) {
            System.out.println("NOT UPDATING horseId " + horseId + " driver Id: " + driverId);
            return;
        }
        try {

            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setInt(1, result);
            stat.setFloat(2, time);
            stat.setString(3, timeModifier);
            stat.setInt(4, driverId);
            stat.setString(5, raceDate);
            stat.setInt(6, raceId);
            stat.setInt(7, raceNumber);
            stat.setInt(8, horseId);
            // stat.setInt(7, lane);
            // stat.setInt(8, distance);

            stat.executeUpdate();

        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

    }

    public void removeRaceIds(int raceId) {
        final String sql = "UPDATE Results SET raceId = -1 WHERE RaceId = ?";
        try {
            final PreparedStatement statement = getConnection().prepareStatement(sql);
            statement.setInt(1, raceId);

            statement.executeUpdate();
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

    }

    public void setSetting(String name, String value) {
        final String sql = "INSERT INTO Settings (name, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = ?";
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setString(1, name);
            stat.setString(2, value);
            stat.setString(3, value);

            stat.execute();
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    public String getSetting(String name) {
        final String sql = "SELECT value FROM Settings WHERE name = ?";

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

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                returnValue = rs.getString("value");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return returnValue;
    }

    public String getDriverLastRace(int driverId, int raceNumber) {
        final String sql
                = "SELECT RaceDate FROM Results WHERE (RaceDate < ? || (RaceDate = ? AND RaceNumber < ?)) AND DriverId = ? ORDER BY RaceDate DESC limit 1";
        String lastRaceDate = "";
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            final String currentDate = MainController.DATE_FORMAT.format(new Date());
            stat.setString(1, currentDate);
            stat.setString(2, currentDate);
            stat.setInt(3, raceNumber);
            stat.setInt(4, driverId);

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                lastRaceDate = rs.getString("RaceDate");
            }
        } catch (final SQLException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        return lastRaceDate;
    }

    public float getAvgTimeByDriverAndDistance(int driverId, int distance, String date, int raceNumber) {
        float returnValue = 0f;

        // Get driver last raceDate
        final String lastRaceDate = getDriverLastRace(driverId, raceNumber);

        final LocalDate lastDate = LocalDate.parse(lastRaceDate);
        final LocalDate parsedDate = LocalDate.parse(date);

        final Long days = Duration.between(lastDate.atStartOfDay(), parsedDate.atStartOfDay()).toDays();

        final String sql
                = "SELECT currentDate.RaceDate as date, prevDate.RaceDate AS prev_date, DATEDIFF(currentDate.RaceDate, prevDate.RaceDate) as diff, "
                        + "ROUND(AVG(currentDate.Time),2) as avgTime, COUNT(*), ROUND(AVG(currentDate.adjTime),2) as adjAvgTime "
                        + "FROM " + "(SELECT (@row_number:=@row_number + 1) as RID, "
                        + "IF (Time = -1, (SELECT MIN(Time) FROM (SELECT Time FROM Results WHERE TimeModifier NOT LIKE '%g%' ORDER BY Time DESC limit 5) abc), time) as adjTime, "
                        + "Results.* FROM Results, "
                        + "(SELECT @row_number:=0) as t WHERE DriverId = ? AND distance = ? AND RaceDate <= ? "
                        + "ORDER BY RaceDate DESC ) AS currentDate "
                        + "INNER JOIN (SELECT (@row_number2 := @row_number2 + 1) AS RID, RaceDate "
                        + "FROM Results, (SELECT @row_number2:=0) AS t "
                        + "WHERE DriverId = ? AND distance = ? AND RaceDate <= ? "
                        + "ORDER BY RaceDate DESC LIMIT 99999 OFFSET 1) AS prevDate ON currentDate.RID = prevDate.RID "
                        + "GROUP BY diff ORDER BY DIFF";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setInt(1, driverId);
            stat.setInt(2, distance);
            stat.setString(3, lastRaceDate);
            stat.setInt(4, driverId);
            stat.setInt(5, distance);
            stat.setString(6, lastRaceDate);

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                if (rs.getInt("diff") == days) {
                    returnValue = rs.getFloat("adjAvgTime");
                    break;
                }
            }

        } catch (final SQLException e) {
            System.out.println("ERROR IN SQL " + sql + " \ndate " + lastRaceDate);
            throw new RuntimeException(e.getMessage(), e);
        }

        return returnValue;
    }

    public void getTestDataToFile() {
        final String sql = "SELECT * FROM Results WHERE RaceDate < ? AND RaceDate > ? ORDER BY RaceDate DESC";

        final String delimiter = ";";
        final String currentDate = MainController.DATE_FORMAT.format(new Date());

        final File file = new File(System.getProperty("user.dir") + File.separator + "src/test/testFiles.csv");
        try (final BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
            final PreparedStatement stat
                    = getConnection().prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            stat.setString(1, currentDate);
            stat.setString(2, "2015-01-01");

            final ResultSet rs = stat.executeQuery();

            rs.last();
            final int lastRow = rs.getRow();
            rs.beforeFirst();

            int i = 1;
            while (rs.next()) {
                final Result result = new Result(rs.getInt(1), rs.getInt(2), rs.getString(3), rs.getString(4), rs.getInt(5),
                        rs.getInt(6), rs.getInt(7), rs.getInt(8), rs.getFloat(9), rs.getString(10), rs.getInt(11), rs.getInt(12),
                        rs.getInt(13), rs.getInt(14), rs.getInt(15), rs.getInt(16));

                final float avgHorseTime = getAvgTime(result.getHorseId(), result.getRaceDate(), -1, false);
                final float avgHorseTimeDistance
                        = getAvgTime(result.getHorseId(), result.getRaceDate(), result.getDistance(), false);
                final float avgDriverTime = getAvgTime(result.getDriverId(), result.getRaceDate(), -1, true);
                final float avgDriverTimeDistance
                        = getAvgTime(result.getDriverId(), result.getRaceDate(), result.getDistance(), true);

                writer.append(result.resultToFileString() + ";" + avgHorseTime + ";" + avgHorseTimeDistance + ";" + avgDriverTime
                        + ";" + avgDriverTimeDistance + System.lineSeparator());

                if (i % 1000 == 0) {
                    System.out.println(LocalTime.now() + " Done with " + i++ + " of " + lastRow);
                }
            }

        } catch (final SQLException | IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

    }

    public int getLastRaceDaysHorse(int horseId, String raceDate, int raceNumber) {
        int returnValue = 999;
        final String sql = "SELECT DATEDIFF(?, startDate.RaceDate) as days, startDate.RaceDate, nextDate.RaceDate, RaceNumber "
                + "FROM (SELECT (@row_number := @row_number + 1) AS orderNum, RaceDate, RaceNumber "
                + "FROM Results, (SELECT @row_number:=0) AS t WHERE RaceDate <= ? AND HorseId = ? "
                + "ORDER BY RaceDate DESC, RaceNumber DESC) AS startDate " + "INNER JOIN "
                + "(SELECT (@row_number2 := @row_number2 + 1) AS orderNum, RaceDate "
                + "FROM Results, (SELECT @row_number2 := 0) AS t WHERE RaceDate <= ? AND HorseId = ? "
                + "ORDER BY RaceDate DESC, RaceNumber DESC " + "LIMIT 11 "
                + "OFFSET 1) AS nextDate ON nextDate.orderNum = startDate.orderNum";
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);

            stat.setString(1, raceDate);
            stat.setString(2, raceDate);
            stat.setInt(3, horseId);
            stat.setString(4, raceDate);
            stat.setInt(5, horseId);
            final ResultSet rs = stat.executeQuery();

            boolean getNext = false;
            while (rs.next()) {
                if (getNext) {
                    returnValue = rs.getInt("days");
                    break;
                }
                if (rs.getString("RaceDate").equals(raceDate) && rs.getInt("RaceNumber") == raceNumber) {
                    getNext = true;
                }
            }
        } catch (final SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return returnValue;
    }

    public int getLastRaceDaysDriver(int driverId, String raceDate, int raceNumber) {
        int returnValue = 999;

        final String sql = "SELECT DATEDIFF(?, startDate.RaceDate) as days, startDate.RaceDate, nextDate.RaceDate, RaceNumber "
                + "FROM (SELECT (@row_number := @row_number + 1) AS orderNum, RaceDate, RaceNumber "
                + "FROM Results, (SELECT @row_number:=0) AS t WHERE RaceDate <= ? AND DriverId = ? "
                + "ORDER BY RaceDate DESC, RaceNumber DESC) AS startDate " + "INNER JOIN "
                + "(SELECT (@row_number2 := @row_number2 + 1) AS orderNum, RaceDate "
                + "FROM Results, (SELECT @row_number2 := 0) AS t WHERE RaceDate <= ? AND DriverId = ? "
                + "ORDER BY RaceDate DESC, RaceNumber DESC " + "LIMIT 11 "
                + "OFFSET 1) AS nextDate ON nextDate.orderNum = startDate.orderNum";

        try {
            final CallableStatement stat = getConnection().prepareCall(sql);

            stat.setString(1, raceDate);
            stat.setString(2, raceDate);
            stat.setInt(3, driverId);
            stat.setString(4, raceDate);
            stat.setInt(5, driverId);

            final ResultSet rs = stat.executeQuery();

            boolean getNext = false;
            while (rs.next()) {
                if (getNext) {
                    returnValue = rs.getInt("days");
                    break;
                }
                if (rs.getString("RaceDate").equals(raceDate) && rs.getInt("RaceNumber") == raceNumber) {
                    getNext = true;
                }
            }
        } catch (final SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return returnValue;
    }

    public int getLastRaceDaysBoth(int horseId, int driverId, String raceDate, int raceNumber) {
        int returnValue = 999;

        final String sql = "SELECT DATEDIFF(?, startDate.RaceDate) as days, startDate.RaceDate, nextDate.RaceDate, RaceNumber "
                + "FROM (SELECT (@row_number := @row_number + 1) AS orderNum, RaceDate, RaceNumber "
                + "FROM Results, (SELECT @row_number:=0) AS t WHERE RaceDate <= ? AND DriverId = ? AND HorseId = ? "
                + "ORDER BY RaceDate DESC, RaceNumber DESC) AS startDate " + "INNER JOIN "
                + "(SELECT (@row_number2 := @row_number2 + 1) AS orderNum, RaceDate "
                + "FROM Results, (SELECT @row_number2 := 0) AS t WHERE RaceDate <= ? AND DriverId = ? AND HorseId = ? "
                + "ORDER BY RaceDate DESC, RaceNumber DESC " + "LIMIT 11 "
                + "OFFSET 1) AS nextDate ON nextDate.orderNum = startDate.orderNum";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);

            stat.setString(1, raceDate);
            stat.setString(2, raceDate);
            stat.setInt(3, driverId);
            stat.setInt(4, horseId);
            stat.setString(5, raceDate);
            stat.setInt(6, driverId);
            stat.setInt(7, horseId);

            final ResultSet rs = stat.executeQuery();

            boolean getNext = false;
            while (rs.next()) {
                if (getNext) {
                    returnValue = rs.getInt("days");
                    break;
                }
                if (rs.getString("RaceDate").equals(raceDate) && rs.getInt("RaceNumber") == raceNumber) {
                    getNext = true;
                }
            }
        } catch (final SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return returnValue;
    }

    public ArrayList<DaysSinceLastRace> getDriverDaysSinceLastRaceStats(int driverId) {
        final ArrayList<DaysSinceLastRace> daysStats = Lists.newArrayList();
        final String sql
                = "SELECT DATEDIFF(startDate.RaceDate, prevDate.RaceDate) as days, count(*) as count, SUM(if (startDate.result = 1,1,0)) as wins, SUM(if (startDate.result = 1,1,0)) / count(*) as percent "
                        + "FROM (SELECT (@row_number := @row_number + 1) AS orderNum, RaceDate, Result, RaceNumber "
                        + "FROM Results, (SELECT @row_number:=0) AS t " + "WHERE DriverId = ? "
                        + "ORDER BY RaceDate DESC, RaceNumber ASC) as startDate " + "INNER JOIN "
                        + "(SELECT (@row_number2 := @row_number2 + 1) AS orderNum, RaceDate "
                        + "FROM Results, (SELECT @row_number2 := 0) AS t  " + "WHERE DriverId = ? "
                        + "ORDER BY RaceDate DESC, RaceNumber ASC " + "LIMIT 100000 "
                        + "OFFSET 1) AS prevDate ON startDate.orderNum = prevDate.orderNum GROUP BY days HAVING days <= 100 ORDER BY days ASC";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);

            stat.setInt(1, driverId);
            stat.setInt(2, driverId);

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                daysStats.add(
                        new DaysSinceLastRace(rs.getInt("days"), rs.getInt("count"), rs.getInt("wins"), rs.getFloat("percent")));
            }

        } catch (final SQLException e) {
            e.printStackTrace();
            throw new RuntimeException();
        }

        return daysStats;
    }

    public DaysSinceLastRace getDriverDaysSinceLastRaceStat(int driverId, int daysSinceLast) {
        DaysSinceLastRace dayStat = null;
        final String sql
                = "SELECT DATEDIFF(startDate.RaceDate, prevDate.RaceDate) as days, count(*) as count, SUM(if (startDate.result = 1,1,0)) as wins, SUM(if (startDate.result = 1,1,0)) / count(*) as percent "
                        + "FROM (SELECT (@row_number := @row_number + 1) AS orderNum, RaceDate, Result, RaceNumber "
                        + "FROM Results, (SELECT @row_number:=0) AS t " + "WHERE DriverId = ? "
                        + "ORDER BY RaceDate DESC, RaceNumber ASC) as startDate " + "INNER JOIN "
                        + "(SELECT (@row_number2 := @row_number2 + 1) AS orderNum, RaceDate "
                        + "FROM Results, (SELECT @row_number2 := 0) AS t  " + "WHERE DriverId = ? "
                        + "ORDER BY RaceDate DESC, RaceNumber ASC " + "LIMIT 100000 "
                        + "OFFSET 1) AS prevDate ON startDate.orderNum = prevDate.orderNum WHERE DATEDIFF(startDate.RaceDate, prevDate.RaceDate) = ?";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);

            stat.setInt(1, driverId);
            stat.setInt(2, driverId);
            stat.setInt(3, daysSinceLast);

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                dayStat = new DaysSinceLastRace(rs.getInt("days"), rs.getInt("count"), rs.getInt("wins"), rs.getFloat("percent"));
            }

        } catch (final SQLException e) {
            e.printStackTrace();
            throw new RuntimeException();
        }

        return dayStat;
    }

    public ArrayList<Integer> getDriverIds() {
        final String sql = "SELECT DISTINCT(DriverId) as id FROM Results WHERE RaceDate > DATE_SUB(RaceDate, INTERVAL 6 MONTH)";
        final ArrayList<Integer> driverIds = Lists.newArrayList();
        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                driverIds.add(rs.getInt("id"));
            }

        } catch (final SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return driverIds;
    }

    public String getLastResults(int id, int id2, String raceDate, int raceNumber, int limit, String type) {
        String returnValue = "";
        final String typeString;
        if (type.equals("Horse")) {
            typeString = "HorseId = ? AND ";
        } else if (type.equals("Driver")) {
            typeString = "DriverId = ? AND ";
        } else if (type.equals("Both")) {
            typeString = "DriverId = ? AND HorseId = ? AND ";
        } else {
            typeString = "";
        }
        final String sql = "SELECT AVG(IF (Result <= 0, 8, result)) as avg, GROUP_CONCAT(Result) as list FROM "
                + "(SELECT result FROM Results " + "WHERE " + typeString + "RaceDate < ? " + "AND RaceDate < DATE(NOW()) "
                + "ORDER BY RaceDate DESC, RaceNumber DESC " + "LIMIT ?) as t";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            if (type.equals("Both")) {
                stat.setInt(1, id);
                stat.setInt(2, id2);
                stat.setString(3, raceDate);
                stat.setInt(4, limit);
            } else {
                stat.setInt(1, id);
                stat.setString(2, raceDate);
                stat.setInt(3, limit);
            }
            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                float avg = rs.getFloat("avg");
                final String dbList = rs.getString("list");
                final ArrayList<String> list;
                if (dbList == null) {
                    list = new ArrayList<>();
                } else {
                    list = Lists.newArrayList(dbList.split(","));
                }
                if (list.size() < limit) {
                    int newAvg = 0;
                    for (final String res : list) {
                        Integer resVal = Integer.valueOf(res);
                        if (resVal <= 0) {
                            resVal = 8;
                        }
                        newAvg += resVal;
                    }
                    for (int i = list.size(); i < limit; i++) {
                        newAvg += 8;
                    }
                    avg = newAvg / (float) limit;
                }
                returnValue = String.valueOf(avg) + " (" + dbList + ")";
            }

        } catch (final SQLException e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
        return returnValue;
    }

    public List<SimpleEntry<Integer, Float>> getTrackAvarages(String trackName, ArrayList<Integer> distances, String date, int id,
            boolean isDriver) {
        final List<SimpleEntry<Integer, Float>> res = Lists.newArrayList();
        String typeString;
        if (id > 0) {
            if (isDriver) {
                typeString = "AND DriverId = ? ";
            } else {
                typeString = "AND HorseId = ? ";
            }
        } else {
            typeString = "";
        }

        String trackFilter;
        if (Strings.isNullOrEmpty(trackName)) {
            trackFilter = "";
        } else {
            trackFilter = "AND TrackId = (SELECT id FROM Track WHERE name = ?) ";
        }

        final String sql = "SELECT Lane, AVG(IF(Time <= 0,30,time)) as avg FROM Results " + "WHERE distance IN (?) " + trackFilter
                + typeString + "AND RaceDate < ? " + "GROUP BY Lane " + "ORDER BY Lane ASC";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            final String in = String.join(",", distances.stream().map(m -> m.toString()).collect(Collectors.toList()));
            stat.setString(1, in);
            if (Strings.isNullOrEmpty(trackName)) {
                if (id > 0) {
                    stat.setInt(2, id);
                    stat.setString(3, date);
                } else {
                    stat.setString(2, date);
                }

            } else {
                stat.setString(2, trackName);
                if (id > 0) {
                    stat.setInt(3, id);
                    stat.setString(4, date);
                } else {
                    stat.setString(3, date);
                }
            }

            final ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                res.add(new SimpleEntry<>(rs.getInt("Lane"), rs.getFloat("avg")));
            }

        } catch (final SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return res;
    }

    public Float getTrackAvarage(String trackName, ArrayList<Integer> distances, String date, int id, boolean isDriver) {
        Float res = 0f;
        String typeString;
        String distanceFilter;
        if (id > 0) {
            if (isDriver) {
                typeString = "DriverId = ? AND ";
            } else {
                typeString = "HorseId = ? AND ";
            }
        } else {
            typeString = "";
        }

        if (distances != null && !distances.isEmpty()) {
            distanceFilter = " distance IN (?) AND ";
        } else {
            distanceFilter = "";
        }

        String trackFilter;
        if (Strings.isNullOrEmpty(trackName)) {
            trackFilter = "";
        } else {
            trackFilter = " TrackId = (SELECT id FROM Track WHERE name = ?) AND ";
        }

        final String sql = "SELECT AVG(IF(Time <= 0,30,time)) as avg FROM Results " + "WHERE " + typeString + distanceFilter
                + trackFilter + "RaceDate < ?";

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);

            if (distances != null && !distances.isEmpty()) {
                final String in = String.join(",", distances.stream().map(m -> m.toString()).collect(Collectors.toList()));

                if (Strings.isNullOrEmpty(trackName)) {
                    if (id > 0) {
                        stat.setInt(1, id);
                        stat.setString(2, in);
                        stat.setString(3, date);

                    } else {
                        stat.setString(1, in);
                        stat.setString(2, date);
                    }
                } else {
                    if (id > 0) {
                        stat.setInt(1, id);
                        stat.setString(2, in);
                        stat.setString(3, trackName);
                        stat.setString(4, date);
                    } else {
                        stat.setString(1, in);
                        stat.setString(2, trackName);
                        stat.setString(3, date);
                    }
                }
            } else {
                if (Strings.isNullOrEmpty(trackName)) {
                    if (id > 0) {
                        stat.setInt(1, id);
                        stat.setString(2, date);
                    } else {
                        stat.setString(1, date);
                    }
                } else {
                    if (id > 0) {
                        stat.setInt(1, id);
                        stat.setString(2, trackName);
                        stat.setString(3, date);
                    } else {
                        stat.setString(1, trackName);
                        stat.setString(2, date);
                    }
                }
            }
            final ResultSet rs = stat.executeQuery();
            while (rs.next()) { // Om inget resultat kör om utan track, markeras??
                res = rs.getFloat("avg");
            }

            if (res <= 0 && !Strings.isNullOrEmpty(trackName)) {
                res = getTrackAvarage("", distances, date, id, isDriver);
                distances = null;
            } else if (res <= 0 && Strings.isNullOrEmpty(trackName) && distances != null && !distances.isEmpty()) {
                res = getTrackAvarage("", null, date, id, isDriver);
            }

        } catch (final SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return res;
    }

    public void setfullStatsCollected(String who, int travsportId, String date) {
        String sql;
        if ("horse".equals(who)) {
            sql = "UPDATE Horse SET fullStatsCollected = ? WHERE travsportId = ?";
        } else if ("driver".equals(who)) {
            sql = "UPDATE Driver SET fullStatsCollected = ? WHERE travsportId = ?";
        } else {
            throw new RuntimeException("Unknown who: " + who);
        }

        try {
            final PreparedStatement stat = getConnection().prepareStatement(sql);
            stat.setString(1, date);
            stat.setInt(2, travsportId);

            stat.executeUpdate();
        } catch (final SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public String getFullStatsCollected(String who, int travsportId) {
        final String sql;
        String result = null;
        if ("horse".equals(who)) {
            sql = "SELECT fullStatsCollected FROM Horse WHERE travsportId = ?";
        } else if ("driver".equals(who)) {
            sql = "SELECT fullStatsCollected FROM Driver WHERE travsportId = ?";
        } else {
            throw new RuntimeException("Failed to get Full stats");
        }

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

            final ResultSet rs = stat.executeQuery();

            while (rs.next()) {
                result = rs.getString("fullStatsCollected");

            }
        } catch (final SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return result;
    }

}
