package controllers;

import java.net.URL;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

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

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.MapValueFactory;
import objects.HorseRaceData;

@SuppressWarnings("rawtypes")
public class AtgController implements Initializable {

	@FXML TableColumn<Map, Integer> raceColumn = new TableColumn<>("Race");
	@FXML TableColumn<Map, String> horseNameColumn = new TableColumn<>("Horse name");
	@FXML TableColumn<Map, Integer> laneColumn = new TableColumn<>("Lane");
	@FXML TableColumn<Map, Integer> distanceColumn = new TableColumn<>("Distance");
	@FXML TableColumn<Map, String> timeModifierColumn = new TableColumn<>("Time modifier");
	@FXML TableColumn<Map, String> shoesColumn = new TableColumn<>("Shoes");
	@FXML TableColumn<Map, String> driverNameColumn = new TableColumn<>("Driver name");
	@FXML TableColumn<Map, String> trainerNameColumn = new TableColumn<>("Trainer name");
	@FXML TableColumn<Map, Integer> resultColumn = new TableColumn<>("Result");
	@FXML TableColumn<Map, String> horseAvgTimeColumn = new TableColumn<>("Horse avg time");
	@FXML TableColumn<Map, String> horseAvgTimeDistanceColumn = new TableColumn<>("Horse avg time distance");
	@FXML TableColumn<Map, String> driverAvgTimeColumn = new TableColumn<>("Driver avg time");
	@FXML TableColumn<Map, String> driverAvgTimeDistanceColumn = new TableColumn<>("Driver Avg time distance");
	@FXML TableColumn<Map, Float> avgTimeColumn = new TableColumn<>("Avg time");
	@FXML TableColumn<Map, Float> winPercentColumn = new TableColumn<>("Lane win percent");
	@FXML TableColumn<Map, String> horseGallopRateColumn = new TableColumn<>("Horse gallop rate");
	@FXML TableColumn<Map, String> driverGallopRateColumn = new TableColumn<>("Driver gallop rate");
	@FXML TableColumn<Map, Float> calculationColumn = new TableColumn<>("Calculated Value");
	@FXML TableColumn<Map, Float> prevCalculationColumn = new TableColumn<>("Previous Calculated Value");
	@FXML TableColumn<Map, String> trackColumn = new TableColumn<>("Track");

	List<SimpleEntry<Integer, Float>> autostartPercentages;
	List<SimpleEntry<Integer, Float>> voltstartPercentages;


	@FXML
	protected TableView<Map<String, Object>> racesTable;

	protected List<HorseRaceData> todaysRaces = new ArrayList<>();

	protected ObservableList<Map<String, Object>> tableRaces;
	private final float horseDistanceAndTypeMultiplyer = 3f; // orig 3f
	private final float horseDistanceMultiplier = 2f;// orig 2f
	private final float driverDistanceAndTypeMultiplyer = 3f;// orig 3f
	private final float driverDistanceMultiplier = 2f;// orig 2f

	@Override
	public void initialize(URL arg0, ResourceBundle arg1) {
		trackColumn.setCellValueFactory(new MapValueFactory<>("track"));
		raceColumn.setCellValueFactory(new MapValueFactory<>("race"));
		horseNameColumn.setCellValueFactory(new MapValueFactory<>("horseName"));
		laneColumn.setCellValueFactory(new MapValueFactory<>("lane"));
		distanceColumn.setCellValueFactory(new MapValueFactory<>("distance"));
		timeModifierColumn.setCellValueFactory(new MapValueFactory<>("timeModifier"));
		shoesColumn.setCellValueFactory(new MapValueFactory<>("shoes"));
		driverNameColumn.setCellValueFactory(new MapValueFactory<>("driverName"));
		trainerNameColumn.setCellValueFactory(new MapValueFactory<>("trainerName"));
		resultColumn.setCellValueFactory(new MapValueFactory<>("result"));
		horseAvgTimeColumn.setCellValueFactory(new MapValueFactory<>("horseAvgTime"));
		horseAvgTimeDistanceColumn.setCellValueFactory(new MapValueFactory<>("horseAvgTimeDistance"));
		driverAvgTimeColumn.setCellValueFactory(new MapValueFactory<>("driverAvgTime"));
		driverAvgTimeDistanceColumn.setCellValueFactory(new MapValueFactory<>("driverAvgTimeDistance"));
		avgTimeColumn.setCellValueFactory(new MapValueFactory<>("avgTime"));
		winPercentColumn.setCellValueFactory(new MapValueFactory<>("winPercent"));
		horseGallopRateColumn.setCellValueFactory(new MapValueFactory<>("horseGallopRate"));
		driverGallopRateColumn.setCellValueFactory(new MapValueFactory<>("driverGallopRate"));
		calculationColumn.setCellValueFactory(new MapValueFactory<>("calculatedValue"));
		prevCalculationColumn.setCellValueFactory(new MapValueFactory<>("prevCalculatedValue"));

	}

	public void updateRacesTable() {
		racesTable.getItems().clear();
		tableRaces = FXCollections.<Map<String, Object>>observableArrayList();
		for (final HorseRaceData horseRaceData : todaysRaces) {
			final Map<String, Object> row = Maps.newHashMap();
			row.put("track", horseRaceData.getTrackName());
			row.put("race", horseRaceData.getRaceNumber());
			row.put("horseName", horseRaceData.getHorseName());
			row.put("lane", horseRaceData.getLane());
			row.put("distance", horseRaceData.getDistance());
			row.put("timeModifier", horseRaceData.getTimeModifier());
			row.put("shoes", horseRaceData.getShoes());
			row.put("driverName", horseRaceData.getDriverName());
			row.put("trainerName", horseRaceData.getTrainerName());
			row.put("result", horseRaceData.getResult());
			row.put("horseAvgTime", horseRaceData.getAvgHorseTime() + "(" + horseRaceData.getAvgHorseTimeCount() + ")");
			row.put("driverAvgTime", horseRaceData.getAvgDriverTime() + "(" + horseRaceData.getAvgDriverTimeCount() + ")");

			row.put("horseAvgTimeDistance", horseRaceData.getAvgHorseTimeByDistance() + "(" + horseRaceData.getAvgHorseTimeByDistanceCount() + ")");
			row.put("driverAvgTimeDistance", horseRaceData.getAvgDriverTimeByDistance() + "(" + horseRaceData.getAvgDriverTimeByDistanceCount() + ")");


			row.put("avgTime", (horseRaceData.getAvgHorseTime() + horseRaceData.getAvgDriverTime()) / 2f);

			final ArrayList<String> distances = Lists.newArrayList();
			todaysRaces.stream()
			.filter(hr -> hr.getRaceNumber() == horseRaceData.getRaceNumber())
			.forEach(hr -> { if (!distances.contains(String.valueOf(horseRaceData.getDistance()))) {
				distances.add(String.valueOf(hr.getDistance()));
			}
			});

			final float laneWinPercent = getLaneWinPercent(horseRaceData, distances);
			try {
				if (horseRaceData.getLane() > 0 && horseRaceData.getTimeModifier().contains("a")) {
					row.put("winPercent", autostartPercentages.get(horseRaceData.getLane()-1).getValue());
				} else {
					row.put("winPercent", voltstartPercentages.get(horseRaceData.getLane()-1).getValue());
				}
			} catch (final IndexOutOfBoundsException e) {
				row.put("winPercent", 0f);
			}

			final SimpleEntry<Integer,Integer> numHorseGallop = DatabaseContoller.getInstance().getNumHorseGallop(horseRaceData.getHorseId());
			final SimpleEntry<Integer,Integer> numDriverGallop = DatabaseContoller.getInstance().getNumDriverGallop(horseRaceData.getDriverId());
			row.put("horseGallopRate", numHorseGallop.getValue() + " ("+Math.round((numHorseGallop.getValue()/Float.valueOf(numHorseGallop.getKey()))*10000)/100+"%) ");
			row.put("driverGallopRate", numDriverGallop.getValue() + " (" + Math.round((numDriverGallop.getValue()/Float.valueOf(numDriverGallop.getKey()))*10000)/100 + "%) ");


			float calcValue = prevCalculation(horseRaceData, laneWinPercent);
			row.put("prevCalculatedValue", calcValue);

			calcValue = calculatedValue(horseRaceData, calcValue);
			row.put("calculatedValue", calcValue);

			tableRaces.add(row);
		}

		racesTable.getItems().addAll(tableRaces);
	}

	protected float getLaneWinPercent(HorseRaceData horseRaceData, ArrayList<String> distances) {
		float laneWinPercent = -1;
		if (horseRaceData.getLane() > 0 && horseRaceData.getTimeModifier().contains("a")) {
			if (autostartPercentages == null) { // || prevRaceDistance != horseRaceData.getDistance()) {
				autostartPercentages = DatabaseContoller.getInstance().getAutostartWinPercents(distances);
			}
			if (horseRaceData.getLane() < autostartPercentages.size()) {
				laneWinPercent = autostartPercentages.get(horseRaceData.getLane()-1).getValue();
			}
		} else if (horseRaceData.getLane() > 0 && !horseRaceData.getTimeModifier().contains("a")) {
			if (voltstartPercentages == null) { // || prevRaceDistance != horseRaceData.getDistance()) {
				voltstartPercentages = DatabaseContoller.getInstance().getVoltstartWinPercents(distances);
			}
			if (horseRaceData.getLane() < voltstartPercentages.size()) {
				laneWinPercent = voltstartPercentages.get(horseRaceData.getLane()-1).getValue();
			}
		}
		return laneWinPercent;
	}

	protected float calculatedValue(final HorseRaceData horseRaceData, float calcValue) {
		float returnValue = calcValue;
		returnValue += calculateCombinedDriverValue(horseRaceData)/2;
		return returnValue;

	}

	protected float prevCalculation(final HorseRaceData horseRaceData, float laneWinPercent) {
		// Calculate Comparison
		float calcValue = calculateCombinedHorseValue(horseRaceData);

		if (laneWinPercent > -1) {
			calcValue -= laneWinPercent / 8;
		}
		return calcValue;
	}

	protected float calculateCombinedHorseValue(HorseRaceData horseRaceData) {
		float returnValue = 0f;
		boolean distanceAndType = false;
		boolean distance = false;
		returnValue += horseRaceData.getAvgHorseTime();
		float divideBy = 1f;
		if (horseRaceData.getAvgHorseTimeByDistanceAndTypeCount() > 0) {
			distanceAndType = true;
			divideBy += horseDistanceAndTypeMultiplyer ;
		}
		if (horseRaceData.getAvgHorseTimeByDistanceCount() > 0) {
			distance = true;
			divideBy += horseDistanceMultiplier;
		}

		if (distanceAndType) {
			returnValue += horseRaceData.getAvgHorseTimeByDistanceAndType() * horseRaceData.getAvgHorseTimeByDistanceAndTypeCount();//* horseDistanceAndTypeMultiplyer;//
			divideBy += horseRaceData.getAvgHorseTimeByDistanceAndTypeCount();
		}
		if (distance) {
			returnValue += horseRaceData.getAvgHorseTimeByDistance() * horseRaceData.getAvgHorseTimeByDistanceCount(); // * horseDistanceMultiplier;//
			divideBy += horseRaceData.getAvgHorseTimeByDistanceCount();
		}

		return returnValue / divideBy;
	}

	protected float calculateCombinedDriverValue(HorseRaceData horseRaceData) {
		float returnValue = 0f;
		boolean distanceAndType = false;
		boolean distance = false;
		returnValue += horseRaceData.getAvgDriverTime();
		float divideBy = 1f;
		if (horseRaceData.getAvgDriverTimeByDistanceAndTypeCount() > 0) {
			distanceAndType = true;
			divideBy += driverDistanceAndTypeMultiplyer;
		}
		if (horseRaceData.getAvgDriverTimeByDistanceCount() > 0) {
			distance = true;
			divideBy += driverDistanceMultiplier;
		}

		if (distanceAndType) {
			returnValue += horseRaceData.getAvgDriverTimeByDistanceAndType() * horseRaceData.getAvgDriverTimeByDistanceAndTypeCount();// * driverDistanceAndTypeMultiplyer;//
			divideBy += horseRaceData.getAvgDriverTimeByDistanceAndTypeCount();
		}
		if (distance) {
			returnValue += horseRaceData.getAvgDriverTimeByDistance() * horseRaceData.getAvgDriverTimeByDistanceCount();// * driverDistanceMultiplier;//
			divideBy += horseRaceData.getAvgDriverTimeByDistanceCount();
		}

		return returnValue / divideBy;
	}

	protected void resetLaneWinPercentages() {
		autostartPercentages = null;
		voltstartPercentages = null;
	}
}
