//package seng302.controllers; // //import javafx.animation.KeyFrame; //import javafx.animation.Timeline; //import javafx.collections.FXCollections; //import javafx.collections.ObservableList; //import javafx.fxml.FXML; //import javafx.fxml.FXMLLoader; //import javafx.geometry.Point2D; //import javafx.scene.Scene; //import javafx.scene.chart.LineChart; //import javafx.scene.chart.NumberAxis; //import javafx.scene.chart.XYChart; //import javafx.scene.chart.XYChart.Series; //import javafx.scene.control.Button; //import javafx.scene.control.CheckBox; //import javafx.scene.control.ComboBox; //import javafx.scene.control.Slider; //import javafx.scene.layout.AnchorPane; //import javafx.scene.layout.Pane; //import javafx.scene.layout.VBox; //import javafx.scene.paint.Color; //import javafx.scene.paint.Paint; //import javafx.scene.shape.Line; //import javafx.scene.text.Text; //import javafx.stage.Stage; //import javafx.stage.StageStyle; //import javafx.util.Duration; //import javafx.util.StringConverter; //import seng302.client.ClientPacketParser; //import seng302.utilities.GeoUtility; //import seng302.controllers.annotations.Annotation; //import seng302.controllers.annotations.ImportantAnnotationController; //import seng302.controllers.annotations.ImportantAnnotationDelegate; //import seng302.controllers.annotations.ImportantAnnotationsState; //import seng302.fxObjects.BoatGroup; //import seng302.fxObjects.MarkGroup; //import seng302.models.*; //import seng302.models.mark.GateMark; //import seng302.models.mark.Mark; //import seng302.models.mark.SingleMark; //import seng302.models.stream.XMLParser; // //import java.io.IOException; //import java.util.*; //import seng302.models.stream.XMLParser.RaceXMLObject.Participant; //import java.util.stream.Collectors; // ///** // * Created by ptg19 on 29/03/17. // */ //public class RaceViewController extends Thread implements ImportantAnnotationDelegate { // // @FXML // private Text windSpeedText; // @FXML // private LineChart raceSparkLine; // @FXML // private NumberAxis sparklineYAxis; // @FXML // private VBox positionVbox; // @FXML // private CheckBox toggleFps; // @FXML // private Text timerLabel; // @FXML // private AnchorPane contentAnchorPane; // @FXML // private Text windArrowText, windDirectionText; // @FXML // private Slider annotationSlider; // @FXML // private Button selectAnnotationBtn; // @FXML // private ComboBox boatSelectionComboBox; // @FXML // private CanvasController includedCanvasController; // // private static ArrayList startingBoats = new ArrayList<>(); // private boolean displayFps; // private Timeline timerTimeline; // private Stage stage; // private static HashMap> sparkLineData = new HashMap<>(); // private static ArrayList racingBoats = new ArrayList<>(); // private ImportantAnnotationsState importantAnnotations; // private Yacht selectedBoat; // // public void initialize() { // // Load a default important annotation state // importantAnnotations = new ImportantAnnotationsState(); // // //Formatting the y axis of the sparkline // raceSparkLine.getYAxis().setRotate(180); // raceSparkLine.getYAxis().setTickLabelRotation(180); // raceSparkLine.getYAxis().setTranslateX(-5); // raceSparkLine.getYAxis().setAutoRanging(false); // sparklineYAxis.setTickMarkVisible(false); // startingBoats = new ArrayList<>(ClientPacketParser.getBoats().values()); // // includedCanvasController.setup(this); // includedCanvasController.initializeCanvas(); // initializeUpdateTimer(); // initialiseFPSCheckBox(); // initialiseAnnotationSlider(); // initialiseBoatSelectionComboBox(); // includedCanvasController.timer.start(); // selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView()); // // } // // // /** // * The important annotations have been changed, update this view // * // * @param importantAnnotationsState The current state of the selected annotations // */ // public void importantAnnotationsChanged(ImportantAnnotationsState importantAnnotationsState) { // this.importantAnnotations = importantAnnotationsState; // setAnnotations((int) annotationSlider.getValue()); // Refresh the displayed annotations // } // // // /** // * Loads the "select annotations" view in a new window // */ // private void loadSelectAnnotationView() { // try { // FXMLLoader fxmlLoader = new FXMLLoader(); // Stage stage = new Stage(); // // // Set controller // ImportantAnnotationController controller = new ImportantAnnotationController(this, // stage); // fxmlLoader.setController(controller); // // // Load FXML and set CSS // fxmlLoader // .setLocation(getClass().getResource("/views/importantAnnotationSelectView.fxml")); // Scene scene = new Scene(fxmlLoader.load(), 469, 298); // scene.getStylesheets().add(getClass().getResource("/css/master.css").toString()); // stage.initStyle(StageStyle.UNDECORATED); // stage.setScene(scene); // stage.show(); // // controller.loadState(importantAnnotations); // // } catch (IOException e) { // System.out.println("[RaceViewController] IO exception"); // } // } // // // private void initialiseFPSCheckBox() { // displayFps = true; // toggleFps.selectedProperty().addListener( // (observable, oldValue, newValue) -> displayFps = !displayFps); // } // // private void initialiseAnnotationSlider() { // annotationSlider.setLabelFormatter(new StringConverter() { // @Override // public String toString(Double n) { // if (n == 0) { // return "None"; // } // if (n == 1) { // return "Important"; // } // if (n == 2) { // return "All"; // } // // return "All"; // } // // @Override // public Double fromString(String s) { // switch (s) { // case "None": // return 0d; // case "Important": // return 1d; // case "All": // return 2d; // // default: // return 2d; // } // } // }); // // annotationSlider.valueProperty().addListener((obs, oldval, newVal) -> // setAnnotations((int) annotationSlider.getValue())); // // annotationSlider.setValue(2); // } // // // /** // * Used to add any new boats into the race that may have started late or not have had data received yet // */ // void updateSparkLine(){ // // Collect the racing boats that aren't already in the chart // ArrayList sparkLineCandidates = startingBoats.stream().filter(yacht -> !sparkLineData.containsKey(yacht.getSourceId()) // && yacht.getPosition() != null & yacht.getPosition() != "-").collect(Collectors.toCollection(ArrayList::new)); // // // Obtain the qualifying boats to set the max on the Y axis // racingBoats = startingBoats.stream().filter(yacht -> // yacht.getPosition() != null & yacht.getPosition() != "-").collect(Collectors.toCollection(ArrayList::new)); // sparklineYAxis.setUpperBound(racingBoats.size() + 1); // // // Create a new data series for new boats // sparkLineCandidates.stream().filter(yacht -> yacht.getPosition() != null).forEach(yacht -> { // Series yachtData = new Series<>(); // yachtData.setName(yacht.getBoatName()); // yachtData.getData().add(new XYChart.Data<>(Integer.toString(yacht.getLegNumber()), 1 + racingBoats.size() - Double.parseDouble(yacht.getPosition()))); // sparkLineData.put(yacht.getSourceId(), yachtData); // }); // // // Lambda function to sort the series in order of leg (later legs shown more to the right) // List> positions = new ArrayList<>(sparkLineData.values()); // Collections.sort(positions, (o1, o2) -> { // Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size()-1).getXValue()); // Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size()-1).getXValue()); // if (leg2 < leg1){ // return 1; // } else { // return -1; // } // }); // // // Adds the new data series to the sparkline (and set the colour of the series) // raceSparkLine.setCreateSymbols(false); // positions.stream().filter(spark -> !raceSparkLine.getData().contains(spark)).forEach(spark -> { // raceSparkLine.getData().add(spark); // spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName())); // }); // } // // // /** // * Updates the yachts sparkline of the desired boat and using the new leg number // * @param yacht The yacht to be updated on the sparkline // * @param legNumber the leg number that the position will be assigned to // */ // public static void updateYachtPositionSparkline(Yacht yacht, Integer legNumber){ // XYChart.Series positionData = sparkLineData.get(yacht.getSourceId()); // positionData.getData().add(new XYChart.Data<>(Integer.toString(legNumber), 1 + racingBoats.size() - Double.parseDouble(yacht.getPosition()))); // } // // // /** // * gets the rgb string of the boats colour to use for the chart via css // * @param boatName boat passed in to get the boats colour // * @return the colour as an rgb string // */ // private String getBoatColorAsRGB(String boatName){ // Color color = Color.WHITE; // for (Yacht yacht: startingBoats){ // if (Objects.equals(yacht.getBoatName(), boatName)){ // color = yacht.getColour(); // } // } // if (color == null){ // return String.format( "#%02X%02X%02X",255,255,255); // } // return String.format( "#%02X%02X%02X", // (int)( color.getRed() * 255 ), // (int)( color.getGreen() * 255 ), // (int)( color.getBlue() * 255 ) ); // } // // // /** // * Initalises a timer which updates elements of the RaceView such as wind direction, boat // * orderings etc.. which are dependent on the info from the stream parser constantly. // * Updates of each of these attributes are called ONCE EACH SECOND // */ // private void initializeUpdateTimer() { // timerTimeline = new Timeline(); // timerTimeline.setCycleCount(Timeline.INDEFINITE); // // Run timer update every second // timerTimeline.getKeyFrames().add( // new KeyFrame(Duration.seconds(1), // event -> { // updateRaceTime(); // updateWindDirection(); //// updateOrder(); // updateBoatSelectionComboBox(); // updateOrder(); // }) // ); // // // Start the timer // timerTimeline.playFromStart(); // } // // // /** // * Iterates over all corners until ones SeqID matches with the boats current leg number. // * Then it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark // * Returns null if no next mark found. // * @param bg The BoatGroup to find the next mark of // * @return The next Mark or null if none found // */ // private Mark getNextMark(BoatGroup bg) { // Integer legNumber = bg.getBoat().getLegNumber(); // // List markSequence = ClientPacketParser.getXmlObject() // .getRaceXML().getCompoundMarkSequence(); // // if (legNumber == 0) { // return null; // } else if (legNumber == markSequence.size() - 1) { // return null; // } // // for (XMLParser.RaceXMLObject.Corner corner : markSequence) { // if (legNumber + 2 == corner.getSeqID()) { // Integer thisCompoundMarkID = corner.getCompoundMarkID(); // // for (Mark mark : ClientPacketParser.getXmlObject().getRaceXML() // .getAllCompoundMarks()) { // if (mark.getCompoundMarkID() == thisCompoundMarkID) { // return mark; // } // } // } // } // // return null; // } // // // /** // * Updates the wind direction arrow and text as from info from the ClientPacketParser // */ // private void updateWindDirection() { // windDirectionText.setText(String.format("%.1f°", ClientPacketParser.getWindDirection())); // windArrowText.setRotate(ClientPacketParser.getWindDirection()); // windSpeedText.setText(String.format("%.1f Knots", ClientPacketParser.getWindSpeed())); // } // // // /** // * Updates the clock for the race // */ // private void updateRaceTime() { // if (ClientPacketParser.isRaceFinished()) { // timerLabel.setFill(Color.RED); // timerLabel.setText("Race Finished!"); // } else { // timerLabel.setText(getTimeSinceStartOfRace()); // } // } // // // /** // * Grabs the boats currently in the race as from the ClientPacketParser and sets them to be selectable // * in the boat selection combo box // */ // private void updateBoatSelectionComboBox() { // ObservableList observableBoats = FXCollections // .observableArrayList(ClientPacketParser.getBoatsPos().values()); // boatSelectionComboBox.setItems(observableBoats); // } // // // /** // * Updates the order of the boats as from the ClientPacketParser and sets them in the boat order // * section // */ // private void updateOrder() { // positionVbox.getChildren().clear(); // positionVbox.getChildren().removeAll(); // positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString()); // // // list of racing boat id // ArrayList participants = ClientPacketParser.getXmlObject().getRaceXML() // .getParticipants(); // ArrayList participantIDs = new ArrayList<>(); // for (Participant p : participants) { // participantIDs.add(p.getsourceID()); // } // // if (ClientPacketParser.isRaceStarted()) { // /* // for (Yacht boat : ClientPacketParser.getBoatsPos().values()) { // System.out.println("Hi tjere" + boat.getBoatName()); // if (participantIDs.contains(boat.getSourceId()) || true // ) { // check if the boat is racing // if (boat.getBoatStatus() == 69) { // 3 is finish status // Text textToAdd = new Text(boat.getPosition() + ". " + // boat.getShortName() + " (Finished)"); // textToAdd.setFill(Paint.valueOf("#d3d3d3")); // positionVbox.getChildren().add(textToAdd); // // } else { // Text textToAdd = new Text(boat.getPosition() + ". " + // boat.getShortName() + " "); // textToAdd.setFill(Paint.valueOf("#d3d3d3")); // textToAdd.setStyle(""); // positionVbox.getChildren().add(textToAdd); // System.out.println("Adding " + textToAdd.getText()); // } // } // } // */ // for (Yacht boat : ClientPacketParser.getBoats().values()){ // Text textToAdd = new Text(boat.getSourceId() + ". " + boat.getShortName() + " "); // textToAdd.setFill(Paint.valueOf("#d3d3d3")); // textToAdd.setStyle(""); // positionVbox.getChildren().add(textToAdd); // } // } else { // for (Yacht boat : ClientPacketParser.getBoats().values()) { // if (participantIDs.contains(boat.getSourceId())) { // check if the boat is racing // Text textToAdd = new Text(boat.getPosition() + ". " + // boat.getShortName() + " "); // textToAdd.setFill(Paint.valueOf("#d3d3d3")); // textToAdd.setStyle(""); // positionVbox.getChildren().add(textToAdd); // } // } // } // } // // // private void updateLaylines(BoatGroup bg) { // // Mark nextMark = getNextMark(bg); // Boolean isUpwind = null; // // Can only calc leg direction if there is a next mark and it is a gate mark // if (nextMark != null) { // if (nextMark instanceof GateMark) { // if (bg.isUpwindLeg(includedCanvasController, nextMark)) { // isUpwind = true; // } else { // isUpwind = false; // } // // for(MarkGroup mg : includedCanvasController.getMarkGroups()) { // // mg.removeLaylines(); // // if (mg.getMainMark().getId() == nextMark.getId()) { // // SingleMark singleMark1 = ((GateMark) nextMark).getSingleMark1(); // SingleMark singleMark2 = ((GateMark) nextMark).getSingleMark2(); // Point2D markPoint1 = includedCanvasController.findScaledXY(singleMark1.getLatitude(), singleMark1.getLongitude()); // Point2D markPoint2 = includedCanvasController.findScaledXY(singleMark2.getLatitude(), singleMark2.getLongitude()); // HashMap angleAndSpeed; // if (isUpwind) { // angleAndSpeed = PolarTable // .getOptimalUpwindVMG(ClientPacketParser.getWindSpeed()); // } else { // angleAndSpeed = PolarTable // .getOptimalDownwindVMG(ClientPacketParser.getWindSpeed()); // } // // Double resultingAngle = angleAndSpeed.keySet().iterator().next(); // // // Point2D boatCurrentPos = new Point2D(bg.getBoatLayoutX(), bg.getBoatLayoutY()); // Point2D gateMidPoint = markPoint1.midpoint(markPoint2); // Integer lineFuncResult = GeoUtility.lineFunction(boatCurrentPos, gateMidPoint, markPoint2); // Line rightLayline = new Line(); // Line leftLayline = new Line(); // if (lineFuncResult == 1) { // rightLayline = makeRightLayline(markPoint2, 180 - resultingAngle, // ClientPacketParser // .getWindDirection()); // leftLayline = makeLeftLayline(markPoint1, 180 - resultingAngle, // ClientPacketParser // .getWindDirection()); // } else if (lineFuncResult == -1) { // rightLayline = makeRightLayline(markPoint1, 180 - resultingAngle, // ClientPacketParser // .getWindDirection()); // leftLayline = makeLeftLayline(markPoint2, 180 - resultingAngle, // ClientPacketParser // .getWindDirection()); // } // // leftLayline.setStrokeWidth(0.5); // leftLayline.setStroke(bg.getBoat().getColour()); // // rightLayline.setStrokeWidth(0.5); // rightLayline.setStroke(bg.getBoat().getColour()); // // bg.setLaylines(leftLayline, rightLayline); // mg.addLaylines(leftLayline, rightLayline); // // } // } // } // } // } // // // private Point2D getPointRotation(Point2D ref, Double distance, Double angle){ // Double newX = ref.getX() + (ref.getX() + distance -ref.getX())*Math.cos(angle) - (ref.getY() + distance -ref.getY())*Math.sin(angle); // Double newY = ref.getY() + (ref.getX() + distance -ref.getX())*Math.sin(angle) + (ref.getY() + distance -ref.getY())*Math.cos(angle); // // return new Point2D(newX, newY); // } // // // public Line makeLeftLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) { // // Point2D ep = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle); // Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY()); // return line; // // } // // // public Line makeRightLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) { // // Point2D ep = getPointRotation(startPoint, 50.0, baseAngle - layLineAngle); // Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY()); // return line; // // } // // // /** // * Initialised the combo box with any boats currently in the race and adds the required listener // * for the combobox to take action upon selection // */ // private void initialiseBoatSelectionComboBox() { // updateBoatSelectionComboBox(); // boatSelectionComboBox.valueProperty().addListener((observable, oldValue, newValue) -> { // //This listener is fired whenever the combo box changes. This means when the values are updated // //We dont want to set the selected value if the values are updated but nothing clicked (null) // if (newValue != null && newValue != selectedBoat) { // Yacht thisYacht = (Yacht) newValue; // setSelectedBoat(thisYacht); // } // }); // } // // // /** // * Display the list of boats in the order they finished the race // */ // private void loadRaceResultView() { // FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/FinishView.fxml")); // // try { // contentAnchorPane.getChildren().removeAll(); // contentAnchorPane.getChildren().clear(); // contentAnchorPane.getChildren().addAll((Pane) loader.load()); // // } catch (javafx.fxml.LoadException e) { // System.err.println(e.getCause()); // } catch (IOException e) { // System.err.println(e); // } // } // // // /** // * Convert seconds to a string of the format mm:ss // * // * @param time the time in seconds // * @return a formatted string // */ // public String convertTimeToMinutesSeconds(int time) { // if (time < 0) { // return String.format("-%02d:%02d", (time * -1) / 60, (time * -1) % 60); // } // return String.format("%02d:%02d", time / 60, time % 60); // } // // private String getTimeSinceStartOfRace() { // String timerString = "0:00"; // if (ClientPacketParser.getTimeSinceStart() > 0) { // String timerMinute = Long.toString(ClientPacketParser.getTimeSinceStart() / 60); // String timerSecond = Long.toString(ClientPacketParser.getTimeSinceStart() % 60); // if (timerSecond.length() == 1) { // timerSecond = "0" + timerSecond; // } // timerString = "-" + timerMinute + ":" + timerSecond; // } else { // String timerMinute = Long.toString(-1 * ClientPacketParser.getTimeSinceStart() / 60); // String timerSecond = Long.toString(-1 * ClientPacketParser.getTimeSinceStart() % 60); // if (timerSecond.length() == 1) { // timerSecond = "0" + timerSecond; // } // timerString = timerMinute + ":" + timerSecond; // } // return timerString; // } // // // boolean isDisplayFps() { // return displayFps; // } // // private void setAnnotations(Integer annotationLevel) { // switch (annotationLevel) { // // No Annotations // case 0: // for (BoatGroup bg : includedCanvasController.getBoatGroups()) { // bg.setVisibility(false, false, false, false, false, false); // } // break; // // Important Annotations // case 1: // for (BoatGroup bg : includedCanvasController.getBoatGroups()) { // bg.setVisibility( // importantAnnotations.getAnnotationState(Annotation.NAME), // importantAnnotations.getAnnotationState(Annotation.SPEED), // importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK), // importantAnnotations.getAnnotationState(Annotation.LEGTIME), // importantAnnotations.getAnnotationState(Annotation.TRACK), // importantAnnotations.getAnnotationState(Annotation.WAKE) // ); // } // break; // // All Annotations // case 2: // for (BoatGroup bg : includedCanvasController.getBoatGroups()) { // bg.setVisibility(true, true, true, true, true, true); // } // break; // } // } // // // /** // * Sets all the annotations of the selected boat to be visible and all others to be hidden // * // * @param yacht The yacht for which we want to view all annotations // */ // private void setSelectedBoat(Yacht yacht) { // for (BoatGroup bg : includedCanvasController.getBoatGroups()) { // //We need to iterate over all race groups to get the matching boat group belonging to this boat if we // //are to toggle its annotations, there is no other backwards knowledge of a yacht to its boatgroup. // if (bg.getBoat().getHullID().equals(yacht.getHullID())) { // updateLaylines(bg); // bg.setIsSelected(true); // selectedBoat = yacht; // } else { // bg.setIsSelected(false); // } // } // } // // void setStage(Stage stage) { // this.stage = stage; // } // // Stage getStage() { // return stage; // } // // /** // * Used for when the boat attempts to add data to the sparkline (first checks if the sparkline contains info on it) // * @param yachtId // * @return // */ // public static boolean sparkLineStatus(Integer yachtId) { // return sparkLineData.containsKey(yachtId); // } // //}