package objects;

import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;

import controllers.DatabaseController;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;

public class RaceTableView extends Tab {

	private final List<HorseRaceData> trackRaces;

	TabPane racesTabs = new TabPane();

	private TableColumn<ComparingResultsData, String> lastRacesColumnDriver;

	private TableColumn<ComparingResultsData, String> lastRacesColumnHorse;

	private TableColumn<ComparingResultsData, String> lastRacesColumnBoth;

	private TableColumn<ComparingResultsData, String> avgTimeColumnHorseChange;

	private TableColumn<ComparingResultsData, String> avgTimeColumnHorse;

	private TableColumn<ComparingResultsData, String> sumAvgTimeColumnHorse;

	private TableColumn<ComparingResultsData, String> avgTimeColumnDriverChange;

	private TableColumn<ComparingResultsData, String> avgTimeColumnDriver;

	private TableColumn<ComparingResultsData, String> sumAvgTimeColumnDriver;

	private TableColumn<ComparingResultsData, String> lastRacesColumnCombined;

	public RaceTableView(SimpleEntry<String,List<HorseRaceData>> trackRaces) {

		setText(trackRaces.getKey());
		this.trackRaces = trackRaces.getValue();
		final List<Integer> raceNumbers = this.trackRaces.stream().map(m -> m.getRaceNumber()).distinct().collect(Collectors.toList());

		racesTabs.setTabMinWidth(30.0);
		this.setContent(racesTabs);

		for (final Integer rn : raceNumbers) {
			addRaceNumberTab(rn, ComparingResultsData.convertHorseRaceDataList(
					this.trackRaces.stream().filter(p -> p.getRaceNumber() == rn).collect(Collectors.toList())));
		}
	}

	private void addRaceNumberTab(Integer rn, List<ComparingResultsData> races) {
		final DatabaseController db = DatabaseController.getInstance();
		final List<Integer> distances = races.stream().map(m -> m.getDistance()).distinct().collect(Collectors.toList());

		final List<LaneWinObject> laneWinPercents;
		if (races.get(0).getTimeModifier().contains("a")) {
			laneWinPercents = db.getAutostartWinPercents(distances);
		} else {
			laneWinPercents = db.getVoltstartWinPercents(distances);
		}

		for (final ComparingResultsData data : races) {
			final Optional<LaneWinObject> laneWin = laneWinPercents.stream().filter(p -> p.getDistance() == data.getDistance() && p.getLane() == data.getLane()).findFirst();
			if (laneWin.isPresent()) {
				data.setLaneWin(laneWin.get().getPercent());
			} else {
				data.setLaneWin(0);
			}
		}

		final Tab raceNumTab = new Tab(rn.toString());
		raceNumTab.setClosable(false);
		raceNumTab.setStyle("");

		final TableView<ComparingResultsData> theTable = createTable();

		// Add extra columns
		addLastResultsColumn(theTable, races, 10);
		addAvgColumns(theTable, races);
		addRatingColumns(theTable, races);

		theTable.getItems().addAll(races);
		raceNumTab.setContent(theTable);
		racesTabs.getTabs().add(raceNumTab);
	}

	private void addRatingColumns(TableView<ComparingResultsData> table, List<ComparingResultsData> races) {
		final TableColumn<ComparingResultsData, Integer> ratingSumHorse = new TableColumn<>("rating Sum Horse");
		ratingSumHorse.setCellValueFactory(new PropertyValueFactory<>("ratingSumHorse"));
		final TableColumn<ComparingResultsData, Integer> ratingSumDriver = new TableColumn<>("rating Sum Driver");
		ratingSumDriver.setCellValueFactory(new PropertyValueFactory<>("ratingSumDriver"));
		final TableColumn<ComparingResultsData, Integer> ratingLastResult = new TableColumn<>("rating Last results");
		ratingLastResult.setCellValueFactory(new PropertyValueFactory<>("ratingLastResultsTotal"));

		final TableColumn<ComparingResultsData, Integer> sumRatingsColumn = new TableColumn<>("Sum Rating");
		sumRatingsColumn.setCellValueFactory(new PropertyValueFactory<>("sumRatings"));

		ratingLastResult.setPrefWidth(30f);
		ratingSumDriver.setPrefWidth(30f);
		ratingSumHorse.setPrefWidth(30f);
		sumRatingsColumn.setPrefWidth(30f);

		final List<SimpleEntry<Integer, Float>> ratingLastResultList = new ArrayList<SimpleEntry<Integer, Float>>();
		final List<SimpleEntry<Integer, Float>> ratingSumHorseList = new ArrayList<SimpleEntry<Integer, Float>>();
		final List<SimpleEntry<Integer, Float>> ratingSumDriverList = new ArrayList<SimpleEntry<Integer, Float>>();
		final List<SimpleEntry<Integer, Float>> sumRating = new ArrayList<SimpleEntry<Integer, Float>>();
		int i = 0;
		for (final ComparingResultsData item : races) {
			ratingLastResultList.add(new SimpleEntry<Integer, Float>(i, item.getCombinedLastRaces()));
			ratingSumHorseList.add(new SimpleEntry<>(i, item.getSumHorse()));
			ratingSumDriverList.add(new SimpleEntry<>(i, item.getSumDriver()));
			sumRating.add(new SimpleEntry<>(i, (item.getCombinedLastRaces() + item.getSumHorse() + item.getSumDriver())));

			i++;
		}

		ratingLastResultList.sort(new Comparator<SimpleEntry<Integer, Float>>() {
			@Override
			public int compare(SimpleEntry<Integer, Float> o1, SimpleEntry<Integer, Float> o2) {
				return o1.getValue().compareTo(o2.getValue());
			}
		});

		ratingSumHorseList.sort(new Comparator<SimpleEntry<Integer, Float>>() {
			@Override
			public int compare(SimpleEntry<Integer, Float> o1, SimpleEntry<Integer, Float> o2) {
				return o1.getValue().compareTo(o2.getValue());
			}
		});

		ratingSumDriverList.sort(new Comparator<SimpleEntry<Integer, Float>>() {
			@Override
			public int compare(SimpleEntry<Integer, Float> o1, SimpleEntry<Integer, Float> o2) {
				return o1.getValue().compareTo(o2.getValue());
			}
		});

		sumRating.sort(new Comparator<SimpleEntry<Integer, Float>>() {
			@Override
			public int compare(SimpleEntry<Integer, Float> o1, SimpleEntry<Integer, Float> o2) {
				return o1.getValue().compareTo(o2.getValue());
			}
		});

		i = 1;
		for (final SimpleEntry<Integer, Float> r : ratingLastResultList) {
			races.get(r.getKey()).setRatingLastResultsTotal(i++);
		}

		i = 1;
		for (final SimpleEntry<Integer, Float> r : ratingSumHorseList) {
			races.get(r.getKey()).setRatingSumHorse(i++);
		}

		i = 1;
		for (final SimpleEntry<Integer, Float> r : ratingSumDriverList) {
			races.get(r.getKey()).setRatingSumDriver(i++);
		}

		i = 1;
		for (final SimpleEntry<Integer, Float> r : sumRating) {
			races.get(r.getKey()).setSumRatings(i++);
		}
		//		for (final ComparingResultsData crd : races) {
		//			crd.setRatingLastResultsTotal(ratingLastResultList.get(i++).getKey());
		//		}

		table.getColumns().add(ratingLastResult);
		table.getColumns().add(ratingSumHorse);
		table.getColumns().add(ratingSumDriver);
		table.getColumns().add(sumRatingsColumn);
	}

	private void addLastResultsColumn(TableView<ComparingResultsData> table, List<ComparingResultsData> races, int count) {
		lastRacesColumnDriver = new TableColumn<>("last Races driver");
		lastRacesColumnDriver.setCellValueFactory(new PropertyValueFactory<>("lastRacesDriver"));
		lastRacesColumnHorse = new TableColumn<>("last Races horse");
		lastRacesColumnHorse.setCellValueFactory(new PropertyValueFactory<>("lastRacesHorse"));
		lastRacesColumnBoth = new TableColumn<>("last Races both");
		lastRacesColumnBoth.setCellValueFactory(new PropertyValueFactory<>("lastRacesBoth"));
		// Add Combined avgTime + Change

		lastRacesColumnCombined = new TableColumn<>("last Combined");
		lastRacesColumnCombined.setCellValueFactory(new PropertyValueFactory<>("combinedLastRaces"));

		final DatabaseController db = DatabaseController.getInstance();

		for (final ComparingResultsData crd : races) {
			final String lastRacesDriver = db.getLastResults(crd.getDriverId(), 0, crd.getRaceDate(), crd.getRaceNumber(), count, "Driver");
			final String lastRacesHorse = db.getLastResults(crd.getHorseId(), 0, crd.getRaceDate(), crd.getRaceNumber(), count, "Horse");
			final String lastRacesBoth = db.getLastResults(crd.getDriverId(), crd.getHorseId(), crd.getRaceDate(), crd.getRaceNumber(), count, "Both");

			crd.setLastRacesDriver(lastRacesDriver);
			crd.setLastRacesHorse(lastRacesHorse);
			crd.setLastRacesBoth(lastRacesBoth);
		}

		table.getColumns().add(lastRacesColumnHorse);
		table.getColumns().add(lastRacesColumnDriver);
		table.getColumns().add(lastRacesColumnBoth);
		table.getColumns().add(lastRacesColumnCombined);
	}

	private void addAvgColumns(TableView<ComparingResultsData> table, List<ComparingResultsData> races) {
		avgTimeColumnHorseChange = new TableColumn<>("Avg time Horse Change");
		avgTimeColumnHorseChange.setCellValueFactory(new PropertyValueFactory<>("avgTimeHorseChange"));

		avgTimeColumnHorse = new TableColumn<>("Avg time Horse");
		avgTimeColumnHorse.setCellValueFactory(new PropertyValueFactory<>("avgTimeHorse"));

		sumAvgTimeColumnHorse = new TableColumn<>("Sum Horse");
		sumAvgTimeColumnHorse.setCellValueFactory(new PropertyValueFactory<>("sumAvgTimeHorse"));

		avgTimeColumnDriverChange = new TableColumn<>("Avg time Driver Change");
		avgTimeColumnDriverChange.setCellValueFactory(new PropertyValueFactory<>("avgTimeDriverChange"));

		avgTimeColumnDriver = new TableColumn<>("Avg time Driver");
		avgTimeColumnDriver.setCellValueFactory(new PropertyValueFactory<>("avgTimeDriver"));

		sumAvgTimeColumnDriver = new TableColumn<>("Sum Driver");
		sumAvgTimeColumnDriver.setCellValueFactory(new PropertyValueFactory<>("sumAvgTimeDriver"));

		final DatabaseController db = DatabaseController.getInstance();

		final ArrayList<Integer> distances = Lists.newArrayList(races.stream().map(m -> m.getDistance()).distinct().collect(Collectors.toList()));

		final String trackName = races.get(0).getTrackName();
		final String raceDate = races.get(0).getRaceDate();
		List<SimpleEntry<Integer, Float>> trackAvaragesHorse = db.getTrackAvarages(trackName, distances, raceDate, -1, false);
		List<SimpleEntry<Integer, Float>> totalAvaragesHorse = db.getTrackAvarages("", distances, raceDate, -1, false);

		List<SimpleEntry<Integer, Float>> trackAvaragesDriver = db.getTrackAvarages(trackName, distances, raceDate, -1, true);
		List<SimpleEntry<Integer, Float>> totalAvaragesDriver = db.getTrackAvarages("", distances, raceDate, -1, true);

		final Float trackAvarageDriver = db.getTrackAvarage(trackName, distances, raceDate, -1, true);
		final Float trackAvarageHorse = db.getTrackAvarage(trackName, distances, raceDate, -1, false);

		for (final ComparingResultsData crd : races) {
			if (!trackName.equals(crd.getTrackName())) {
				trackAvaragesHorse = db.getTrackAvarages(trackName, distances, crd.getRaceDate(), -1, false);
				totalAvaragesHorse = db.getTrackAvarages("", distances, crd.getRaceDate(), -1, false);

				trackAvaragesDriver = db.getTrackAvarages(trackName, distances, crd.getRaceDate(), -1, true);
				totalAvaragesDriver = db.getTrackAvarages("", distances, crd.getRaceDate(), -1, true);

			}
			// byt till track avg Horse, behöver inte vara lane specifikt Gäller hela funktionen... eller skapa en ny
			final Float valueHorse2 = db.getTrackAvarage(trackName, distances, crd.getRaceDate(), crd.getHorseId(), false) - trackAvarageHorse;
			//			final Float valueHorse = getAvgValueHorse(db, distances, trackAvaragesHorse, crd);
			crd.setAvgTimeHorseChange(valueHorse2);

			final Float valueDriver2 = db.getTrackAvarage(trackName, distances, crd.getRaceDate(), crd.getDriverId(), true) - trackAvarageDriver;
			//			final Float valueDriver = getAvgValueDriver(db, distances, trackAvaragesDriver, crd);
			crd.setAvgTimeDriverChange(valueDriver2);

			final Float trackAvarageDriverValue = db.getTrackAvarage(trackName, distances, crd.getRaceDate(), crd.getDriverId(), true);
			crd.setAvgTimeDriver(trackAvarageDriverValue);
			final Float trackAvarageHorseValue = db.getTrackAvarage(trackName, distances, crd.getRaceDate(), crd.getHorseId(), false);
			crd.setAvgTimeHorse(trackAvarageHorseValue);

			crd.setSumAvgTimeHorse(trackAvarageHorseValue + valueHorse2);
			crd.setSumAvgTimeDriver(trackAvarageDriverValue + valueDriver2);
		}


		table.getColumns().add(avgTimeColumnHorse);
		table.getColumns().add(avgTimeColumnHorseChange);
		table.getColumns().add(sumAvgTimeColumnHorse);
		table.getColumns().add(avgTimeColumnDriver);
		table.getColumns().add(avgTimeColumnDriverChange);
		table.getColumns().add(sumAvgTimeColumnDriver);
	}

	private Float getAvgValueHorse(final DatabaseController db, final ArrayList<Integer> distances,
			final List<SimpleEntry<Integer, Float>> trackAvarages, final ComparingResultsData crd) {

		final Optional<SimpleEntry<Integer, Float>> trackAvgLane = trackAvarages.stream().filter(p -> p.getKey() == crd.getLane()).findFirst();
		Float laneAvg;
		if (trackAvgLane.isPresent()) {
			laneAvg = trackAvgLane.get().getValue();
		} else {
			final List<Float> l = trackAvarages.stream().map(m -> m.getValue()).sorted().collect(Collectors.toList());
			if (l.size() == 0) {
				laneAvg = 15f;
			} else {
				laneAvg = l.get(l.size() - 1);
			}
		}

		final List<SimpleEntry<Integer, Float>> horseAvges = db.getTrackAvarages(crd.getTrackName(), distances, crd.getRaceDate(), crd.getHorseId(), false);
		final Optional<SimpleEntry<Integer, Float>> horseAvg = horseAvges.stream().filter(p -> p.getKey() == crd.getLane()).findFirst();

		if (horseAvg.isPresent() && horseAvg.get().getValue() > 0f) {
			// Om hästens snitt tid för spåret
			laneAvg = horseAvg.get().getValue() - laneAvg;
		} else {
			// FIXME Här hämtas hästens allmäna tid ut, markera detta??
			final List<SimpleEntry<Integer, Float>> horseAvges2 = db.getTrackAvarages("", distances, crd.getRaceDate(), crd.getHorseId(), false);
			final Optional<SimpleEntry<Integer, Float>> horseAvg2 = horseAvges.stream().filter(p -> p.getKey() == crd.getLane()).findFirst();
			if (horseAvg2.isPresent() && horseAvg2.get().getValue() > 0f) {
				laneAvg = horseAvg2.get().getValue() - laneAvg;
			} else {
				// Hästen helt oprövad på distances, markeras hur?
				final float newVal;
				final double sum = horseAvges2.stream().mapToDouble(m -> m.getValue() > 0?m.getValue():30f).sum();
				if (sum > 0) {
					newVal = (float) (sum / horseAvges2.size());
				} else {
					//						newVal = (float)totalAvarages.stream().mapToDouble(m -> m.getValue()).max().orElse(30f);
					newVal = 30f;
				}
				laneAvg = newVal - laneAvg;
			}
		}
		return laneAvg;
	}



	private Float getAvgValueDriver(final DatabaseController db, final ArrayList<Integer> distances,
			final List<SimpleEntry<Integer, Float>> trackAvarages, final ComparingResultsData crd) {

		final Optional<SimpleEntry<Integer, Float>> first = trackAvarages.stream().filter(p -> p.getKey() == crd.getLane()).findFirst();
		Float value;
		if (first.isPresent()) {
			value = first.get().getValue();
		} else {
			final List<Float> l = trackAvarages.stream().map(m -> m.getValue()).sorted().collect(Collectors.toList());
			value = l.get(l.size() - 1);
		}

		final List<SimpleEntry<Integer, Float>> driverAvges = db.getTrackAvarages(crd.getTrackName(), distances, crd.getRaceDate(), crd.getDriverId(), true);
		final Optional<SimpleEntry<Integer, Float>> driverAvg = driverAvges.stream().filter(p -> p.getKey() == crd.getLane()).findFirst();

		if (driverAvg.isPresent() && driverAvg.get().getValue() > 0f) {
			value = driverAvg.get().getValue() - value;
		} else {
			// FIXME Här hämtas hästens allmäna tid ut, markera detta??
			final List<SimpleEntry<Integer, Float>> driverAvges2 = db.getTrackAvarages("", distances, crd.getRaceDate(), crd.getDriverId(), true);
			final Optional<SimpleEntry<Integer, Float>> driverAvg2 = driverAvges.stream().filter(p -> p.getKey() == crd.getLane()).findFirst();
			if (driverAvg2.isPresent() && driverAvg2.get().getValue() > 0f) {
				value = driverAvg2.get().getValue() - value;
			} else {
				// Hästen helt oprövad på distances, markeras hur?
				final float newVal;
				final double sum = driverAvges2.stream().mapToDouble(m -> m.getValue() > 0?m.getValue():30f).sum();
				if (sum > 0) {
					newVal = (float) (sum / driverAvges2.size());
				} else {
					//						newVal = (float)totalAvarages.stream().mapToDouble(m -> m.getValue()).max().orElse(30f);
					newVal = 30f;
				}
				value = newVal - value;
			}
		}
		return value;
	}

	private TableView<ComparingResultsData> createTable() {

		final TableView<ComparingResultsData> table = new TableView<ComparingResultsData>();

		final TableColumn<ComparingResultsData, String> horseNameColumn = new TableColumn<>("Horse name");
		horseNameColumn.setCellValueFactory(new PropertyValueFactory<>("horseName"));
		final TableColumn<ComparingResultsData, Integer> laneColumn = new TableColumn<>("Lane");
		laneColumn.setCellValueFactory(new PropertyValueFactory<>("lane"));
		final TableColumn<ComparingResultsData, Integer> distanceColumn = new TableColumn<>("Distance");
		distanceColumn.setCellValueFactory(new PropertyValueFactory<>("distance"));
		final TableColumn<ComparingResultsData, String> timeModifierColumn = new TableColumn<>("Time modifier");
		timeModifierColumn.setCellValueFactory(new PropertyValueFactory<>("timeModifier"));
		final TableColumn<ComparingResultsData, String> driverNameColumn = new TableColumn<>("Driver name");
		driverNameColumn.setCellValueFactory(new PropertyValueFactory<>("driverName"));
		final TableColumn<ComparingResultsData, String> trainerNameColumn = new TableColumn<>("Trainer name");
		trainerNameColumn.setCellValueFactory(new PropertyValueFactory<>("trainerName"));
		final TableColumn<ComparingResultsData, String> laneWinColumn = new TableColumn<>("Lane win %");
		laneWinColumn.setCellValueFactory(new PropertyValueFactory<>("laneWin"));
		final TableColumn<ComparingResultsData, Integer> resultColumn = new TableColumn<>("Result");
		resultColumn.setCellValueFactory(new PropertyValueFactory<>("result"));

		table.getColumns().add(horseNameColumn);
		table.getColumns().add(driverNameColumn);
		table.getColumns().add(trainerNameColumn);
		table.getColumns().add(distanceColumn);
		table.getColumns().add(laneColumn);
		table.getColumns().add(timeModifierColumn);
		table.getColumns().add(laneWinColumn);
		table.getColumns().add(resultColumn);

		return table;
	}

}
