/*
 * Copyright 2013 Jason Winnebeck
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gillius.jfxutils.examples;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
import org.gillius.jfxutils.JFXUtil;
import org.gillius.jfxutils.chart.ChartPanManager;
import org.gillius.jfxutils.chart.JFXChartUtil;

public class Charting extends Application {
	public static void main( String[] args ) {
		launch( args );
	}

	@FXML
	private LineChart<Number, Number> chart;

	@FXML
	private Slider valueSlider;

	@FXML
	private Label outputLabel;

	private XYChart.Series<Number, Number> series;

	private long startTime;

	private Timeline addDataTimeline;

	@FXML
	void addSample() {
		series.getData().add( new XYChart.Data<Number, Number>( System.currentTimeMillis() - startTime,
		                                                        valueSlider.getValue() ) );
	}

	@FXML
	void autoZoom() {
		chart.getXAxis().setAutoRanging( true );
		chart.getYAxis().setAutoRanging( true );
	}

	@FXML
	void toggleAdd() {
		switch ( addDataTimeline.getStatus() ) {
			case PAUSED:
			case STOPPED:
				addDataTimeline.play();
				chart.getXAxis().setAutoRanging( true );
				chart.getYAxis().setAutoRanging( true );
				//Animation looks horrible if we're updating a lot
				chart.setAnimated( false );
				chart.getXAxis().setAnimated( false );
				chart.getYAxis().setAnimated( false );
				break;
			case RUNNING:
				addDataTimeline.stop();
				//Return the animation since we're not updating a lot
				chart.setAnimated( true );
				chart.getXAxis().setAnimated( true );
				chart.getYAxis().setAnimated( true );
				break;

			default:
				throw new AssertionError( "Unknown status" );
		}
	}

	@Override
	public void start( Stage stage ) throws Exception {
		FXMLLoader loader = new FXMLLoader( getClass().getResource( "Charting.fxml" ) );
		Region contentRootRegion = (Region) loader.load();

		StackPane root = JFXUtil.createScalePane( contentRootRegion, 960, 540, false );
		Scene scene = new Scene( root, root.getPrefWidth(), root.getPrefHeight() );
		stage.setScene( scene );
		stage.setTitle( "Charting Example" );
		stage.show();
	}

	@FXML
	void initialize() {
		startTime = System.currentTimeMillis();

		series = new XYChart.Series<Number, Number>();
		series.setName( "Data" );

		chart.getData().add( series );

		addDataTimeline = new Timeline( new KeyFrame(
				Duration.millis( 250 ),
				new EventHandler<ActionEvent>() {
					@Override
					public void handle( ActionEvent actionEvent ) {
						addSample();
					}
				}
		));
		addDataTimeline.setCycleCount( Animation.INDEFINITE );

		chart.setOnMouseMoved( new EventHandler<MouseEvent>() {
			@Override
			public void handle( MouseEvent mouseEvent ) {
				double xStart = chart.getXAxis().getLocalToParentTransform().getTx();
				double axisXRelativeMousePosition = mouseEvent.getX() - xStart;
				outputLabel.setText( String.format(
						"%d, %d (%d, %d); %d - %d",
						(int) mouseEvent.getSceneX(), (int) mouseEvent.getSceneY(),
						(int) mouseEvent.getX(), (int) mouseEvent.getY(),
						(int) xStart,
						chart.getXAxis().getValueForDisplay( axisXRelativeMousePosition ).intValue()
				) );
			}
		} );

		//Panning works via either secondary (right) mouse or primary with ctrl held down
		ChartPanManager panner = new ChartPanManager( chart );
		panner.setMouseFilter( new EventHandler<MouseEvent>() {
			@Override
			public void handle( MouseEvent mouseEvent ) {
				if ( mouseEvent.getButton() == MouseButton.SECONDARY ||
						 ( mouseEvent.getButton() == MouseButton.PRIMARY &&
						   mouseEvent.isShortcutDown() ) ) {
					//let it through
				} else {
					mouseEvent.consume();
				}
			}
		} );
		panner.start();

		//Zooming works only via primary mouse button without ctrl held down
		JFXChartUtil.setupZooming( chart, new EventHandler<MouseEvent>() {
			@Override
			public void handle( MouseEvent mouseEvent ) {
				if ( mouseEvent.getButton() != MouseButton.PRIMARY ||
				     mouseEvent.isShortcutDown() )
					mouseEvent.consume();
			}
		} );
	}
}

@@ -771,24 +771,107 @@
 pplication;%0A
+import javafx.collections.FXCollections;%0Aimport javafx.collections.ObservableList;%0A
 import javaf
@@ -2161,24 +2161,503 @@
 ng( true );%0A
+%09%09//There seems to be some bug, even with the default NumberAxis, that simply setting the%0A%09%09//auto ranging does not recompute the ranges. So we clear all chart data then re-add it.%0A%09%09//Hopefully I find a more proper way for this, unless it's really bug, in which case I hope%0A%09%09//it gets fixed.%0A%09%09ObservableList%3CXYChart.Series%3CNumber,Number%3E%3E data = chart.getData();%0A%09%09chart.setData( FXCollections.%3CXYChart.Series%3CNumber, Number%3E%3EemptyObservableList() );%0A%09%09chart.setData( data );%0A
 %09%7D%0A%0A%09@FXML%0A%09

Add workaround for what appears to be a bug in ValueAxis or XYChart (or I can't find the proper way) to actually trigger auto-scaling. Simply setting autoRanging to true is not enough, the axes change only partially. So setting the property true and causing the data to refresh (even though it's the same data) triggers a true recalculation of the range.