package test;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import controllers.DatabaseController;
import objects.LaneWinObject;

public class Test {

	/**
	 * Tests needed
	 * Form (last eg. 5), results and time
	 * Days since last race (Horse)
	 * Days since last race (Driver)
	 * Races with current driver
	 *
	 * Vad ska man göra med gallopp
	 * Temperature (needs alot og work, dont have temp data)
	 *
	 */

	String filename = this.getClass().getResource("dataset.csv").getFile();
	private CSVParser parser;
	List<TestDataRow> data = Lists.newArrayList();

	final HashMap<ArrayList<String>, List<LaneWinObject>> autostartWinPercentages = Maps.newHashMap();
	final HashMap<ArrayList<String>, List<LaneWinObject>> voltstartWinPercentages = Maps.newHashMap();

	public void readFile() {
		final Character delimiter = ',';
		BufferedReader br;
		try {
			br = new BufferedReader(new FileReader(filename));
			parser = CSVFormat.DEFAULT.withDelimiter(delimiter).withHeader().parse(br);

			for (final CSVRecord record : parser) {
				final TestDataRow row = new TestDataRow(record.toMap());
				data.add(row);
			}
		} catch (final IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	public float getPercentagesOrderedBy(int topCount, String sortVariable) {

		if(data.isEmpty()) {
			readFile();
		}


		// sort by RaceDate, TrackId, RaceNumber

		Collections.sort(data, new TestDataRow.RaceDateSorter()
				.thenComparing(new TestDataRow.TrackIdSorter())
				.thenComparing(new TestDataRow.RaceNumberSorter()));

		int correctHorseByDistance = 0;
		int correctHorse= 0;
		int correctDriver = 0;
		int correctDriverByDistance = 0;
		int correctCustom = 0;
		int correctCombined = 0;
		int racesCount = 0;
		for (int i = 0; i < data.size(); i++) {
			final TestDataRow firstRow = data.get(i);
			final List<TestDataRow> currentRaces = data.stream().filter(d -> firstRow.getRaceDate().equals(d.getRaceDate()) &&
					firstRow.getTrackId() == d.getTrackId() &&
					firstRow.getRaceNumber() == d.getRaceNumber()).collect(Collectors.toList());

			if (currentRaces.size() < 7) { // Not enough info
				continue;
			}

			final ArrayList<String> distances = Lists.newArrayList();

			currentRaces.forEach(cr -> {
				final String distance = String.valueOf(cr.getDistance());
				if(!distances.contains(distance)) {
					distances.add(distance);
				}
			});

			if (firstRow.getRaceNumber() >= 13) {
				i = i + currentRaces.size();
				continue;
			}

			racesCount++;
			final boolean autostart = firstRow.getTimeModifier().contains("a");
			currentRaces.forEach(cr -> cr.setLaneWinPercent(getWinPercentage(distances, cr.getLane(), cr.getDistance(), autostart)));

			correctHorseByDistance = checkHorseByDistanceCorrect(topCount, correctHorseByDistance, currentRaces, new TestDataRow.horseAvgTimeByDistanceSorter());
			correctHorse = checkHorseByDistanceCorrect(topCount, correctHorse, currentRaces, new TestDataRow.horseAvgTimeSorter());
			correctDriver = checkHorseByDistanceCorrect(topCount, correctDriver, currentRaces, new TestDataRow.driverAvgTimeSorter());
			correctDriverByDistance = checkHorseByDistanceCorrect(topCount, correctDriverByDistance, currentRaces, new TestDataRow.driverAvgTimeByDistanceSorter());

			correctCustom = checkHorseByDistanceCorrect(topCount, correctCustom, currentRaces, new customSorter());
			correctCombined = checkHorseByDistanceCorrect(topCount, correctCombined, currentRaces, new CombinedSorter(1f,1f, 1f, 1f));
			i = i + currentRaces.size();
		}

		System.out.println("total races count " + racesCount + " correctHorse " + correctHorse + " percentage " + correctHorse/(racesCount * 1f));
		System.out.println("total races count " + racesCount + " correctHorseByDistance " + correctHorseByDistance + " percentage " + correctHorseByDistance/(racesCount * 1f));
		System.out.println("total races count " + racesCount + " correctDriver " + correctDriver + " percentage " + correctDriver /(racesCount * 1f));
		System.out.println("total races count " + racesCount + " correctDriverByDistance " + correctDriverByDistance + " percentage " + correctDriverByDistance/(racesCount * 1f));

		System.out.println("total races count " + racesCount + " CustomSorter " + correctCustom + " percentage " + correctCustom/(racesCount * 1f));

		System.out.println("total races count " + racesCount + " CombinedSorter " + correctCombined + " percentage " + correctCombined/(racesCount * 1f));
		// horseAvgTime approx 24,6%
		// driverAvgTime approc 25,6%
		// horseAvgTimeByDistance 27,3%
		// driverAvgTimeByDistance 22,1%
		// ((horseTime + horseTimeByDistance) / x) + ((driverTime + driverTimeByDistance) / y) 27.1
		return correctHorseByDistance/(racesCount * 1f);
	}

	private int checkHorseByDistanceCorrect(int topCount, int correct, final List<TestDataRow> currentRaces, Comparator<TestDataRow> sorter) {
		currentRaces.sort(sorter);

		for (int p = 0; p < topCount; p++) {
			if (currentRaces.get(p).getResult() == 1) {
				correct++;
				break;
			}
		}
		return correct;
	}


	private float getWinPercentage(ArrayList<String> distances, int lane, int distance, boolean autostart) {
		float winPercentage;
		try {
			if (autostart) {
				if (!autostartWinPercentages.containsKey(distances)) {
					autostartWinPercentages.put(distances, DatabaseController.getInstance().getAutostartWinPercents(distances));
				}
				final Optional<LaneWinObject> lw = autostartWinPercentages.get(distances).stream().filter(p -> p.getDistance() == distance && p.getLane() == lane).findFirst();
				if (lw.isPresent()) {
					winPercentage = lw.get().getPercent();
				} else {
					System.out.println("Auto No percentage found for lane " + lane + " and distance " + distance);
					winPercentage = 0f;
				}
			} else {
				if (!voltstartWinPercentages.containsKey(distances)) {
					voltstartWinPercentages.put(distances, DatabaseController.getInstance().getVoltstartWinPercents(distances));
				}
				final Optional<LaneWinObject> lw = voltstartWinPercentages.get(distances).stream().filter(p -> p.getDistance() == distance && p.getLane() == lane).findFirst();
				if (lw.isPresent()) {
					winPercentage = lw.get().getPercent();
				} else {
					System.out.println("Volt No percentage found for lane " + lane + " and distance " + distance);
					winPercentage = 0f;
				}
			}
		} catch (final IndexOutOfBoundsException e) {
			winPercentage = 0f;
		}
		return winPercentage;
	}


	public static class CombinedSorter implements Comparator<TestDataRow> {

		float horseWeight = 1f;
		float driverWeight = 1f;

		float horseLaneWin = 1f;
		float driverLaneWin = 1f;

		public CombinedSorter(float horseWeight, float driverWeight, float horseLaneWin, float driverLaneWin) {
			this.horseWeight = horseWeight;
			this.driverWeight = driverWeight;
			this.horseLaneWin = horseLaneWin;
			this.driverLaneWin = driverLaneWin;
		}
		@Override
		public int compare(TestDataRow o1, TestDataRow o2) {
			final float o1Time = (o1.getHorseAvgTime() * horseLaneWin * horseWeight) + (o1.getDriverAvgTime() * driverLaneWin * driverWeight) - o1.getLaneWinPercent();
			final float o2Time = (o2.getHorseAvgTime() * horseLaneWin * horseWeight) + (o2.getDriverAvgTime() * driverLaneWin * driverWeight) - o2.getLaneWinPercent();
			return Float.compare(o1Time, o2Time);
		}
	}

	public static class customSorter implements Comparator<TestDataRow> {
		@Override
		public int compare(TestDataRow d1, TestDataRow d2) {
			float horseTimed1 = 0f;
			int d1DivideHorse = 0;
			if (d1.getHorseAvgTime() > 0) {
				horseTimed1 += d1.getHorseAvgTime();
				d1DivideHorse++;
			} else {
				horseTimed1 += 5;
			}
			if (d1.getHorseAvgTimeByDistance() > 0) {
				horseTimed1 += d1.getHorseAvgTimeByDistance();
				d1DivideHorse++;
			} else {
				horseTimed1 += 5;
			}

			float horseTimed2 = 0f;
			int d2DivideHorse = 0;
			if (d2.getHorseAvgTime() > 0) {
				horseTimed2 += d2.getHorseAvgTime();
				d2DivideHorse++;
			} else {
				horseTimed2 += 5;
			}
			if (d2.getHorseAvgTimeByDistance() > 0) {
				horseTimed2 += d2.getHorseAvgTimeByDistance();
				d2DivideHorse++;
			} else {
				horseTimed2 += 5;
			}


			float driverTimed1 = 0f;
			int d1DivideDriver = 0;
			if (d1.getDriverAvgTime() > 0) {
				driverTimed1 += d1.getDriverAvgTime();
				d1DivideDriver++;
			} else {
				driverTimed1 += 2;
			}
			if (d1.getDriverAvgTimeByDistance() > 0) {
				driverTimed1 += d1.getDriverAvgTimeByDistance();
				d1DivideDriver++;
			} else {
				driverTimed1 += 2;
			}

			float driverTimed2 = 0f;
			int d2DivideDriver = 0;
			if (d2.getDriverAvgTime() > 0) {
				driverTimed2 += d2.getDriverAvgTime();
				d2DivideDriver++;
			} else {
				driverTimed2 += 2;
			}
			if (d2.getDriverAvgTimeByDistance() > 0) {
				driverTimed2 += d2.getDriverAvgTimeByDistance();
				d2DivideDriver++;
			} else {
				driverTimed2 += 2;
			}

			final float d1Time = ((horseTimed1 - d1.getLaneWinPercent()) / d1DivideHorse) + ((driverTimed1 - d1.getLaneWinPercent()) / d1DivideDriver * 2);
			final float d2Time = ((horseTimed2 - d2.getLaneWinPercent()) / d2DivideHorse) + ((driverTimed2 - d2.getLaneWinPercent()) / d2DivideDriver * 2);

			return Float.compare(d1Time, d2Time);
		}
	}
}

