/*
 * Copyright (c) 2013-2017 Chris Newland.
 * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD
 * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki
 */
package org.adoptopenjdk.jitwatch.ui.stage;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.adoptopenjdk.jitwatch.model.IMetaMember;
import org.adoptopenjdk.jitwatch.ui.main.ICompilationChangeListener;
import org.adoptopenjdk.jitwatch.ui.report.ReportStage;
import org.adoptopenjdk.jitwatch.util.UserInterfaceUtil;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

public class StageManager
{
	private static Map<Stage, List<Stage>> openStages = new HashMap<>();

	private static List<IStageClosedListener> listenerStageClosed = new ArrayList<>();
	private static List<ICompilationChangeListener> listenerCompilationChanged = new ArrayList<>();

	private StageManager()
	{
	}

	public static void registerStageClosedListener(IStageClosedListener listener)
	{
		listenerStageClosed.add(listener);
	}

	public static void registerCompilationChangeListener(ICompilationChangeListener listener)
	{
		listenerCompilationChanged.add(listener);
	}

	public static void notifyCompilationChanged(IMetaMember member)
	{
		for (ICompilationChangeListener listener : listenerCompilationChanged)
		{
			listener.compilationChanged(member);
		}
	}

	private static void notifyStageClosedListeners(Stage stage)
	{
		for (IStageClosedListener listener : listenerStageClosed)
		{
			listener.handleStageClosed(stage);

			if (stage instanceof ICompilationChangeListener)
			{
				listenerCompilationChanged.remove((ICompilationChangeListener)stage);
			}
		}
	}

	public static void clearReportStages()
	{
		for (Stage rootStage : openStages.keySet())
		{
			clearChildren(rootStage);
		}
	}

	public static void clearChildren(Stage stage)
	{
		if (openStages.containsKey(stage))
		{
			for (Stage childStage : openStages.get(stage))
			{
				clearChildren(childStage);
			}
		}

		if (stage instanceof ReportStage)
		{
			((ReportStage) stage).clear();
		}
	}

	// Adds a close buttong to stages
	// for fullscreen JavaFX systems with no window decorations
	private static void addCloseButton(final Stage stage)
	{
		Scene scene = stage.getScene();

		Parent rootNode = scene.getRoot();

		Button btnClose = new Button("X");

		btnClose.setOnAction(new EventHandler<ActionEvent>()
		{
			@Override
			public void handle(ActionEvent e)
			{
				closeStage(stage);
			}
		});

		HBox hbox = new HBox();
		hbox.setSpacing(16);
		hbox.getChildren().addAll(btnClose, new Label(stage.getTitle()));

		if (rootNode instanceof BorderPane)
		{
			BorderPane pane = (BorderPane) rootNode;

			Node topNode = pane.getTop();

			if (topNode instanceof VBox)
			{
				VBox vbox = (VBox) topNode;
				vbox.getChildren().add(0, hbox);
			}
			else
			{
				VBox newTopNode = new VBox();
				newTopNode.setPadding(new Insets(0));

				newTopNode.getChildren().addAll(hbox, topNode);
				pane.setTop(newTopNode);
			}
		}
		else
		{
			VBox newTopNode = new VBox();
			newTopNode.setPadding(new Insets(0));

			newTopNode.getChildren().addAll(hbox, rootNode);

			scene.setRoot(newTopNode);
		}
	}

	public static void addAndShow(final Stage parent, final Stage childStage)
	{
		addAndShow(parent, childStage, null);

	}

	public static void addAndShow(final Stage parent, final Stage childStage, final IStageClosedListener closedListener)
	{
		List<Stage> childrenOfParent = openStages.get(parent);

		if (childrenOfParent == null)
		{
			childrenOfParent = new ArrayList<>();
			openStages.put(parent, childrenOfParent);
		}

		childrenOfParent.add(childStage);

		if (childStage instanceof ICompilationChangeListener)
		{
			registerCompilationChangeListener((ICompilationChangeListener)childStage);
		}
		
		if (UserInterfaceUtil.ADD_CLOSE_DECORATION)
		{
			addCloseButton(childStage);
		}

		childStage.setOnCloseRequest(new EventHandler<WindowEvent>()
		{
			@Override
			public void handle(WindowEvent arg0)
			{
				if (closedListener != null)
				{
					closedListener.handleStageClosed(childStage);
				}

				closeStage(childStage);
			}
		});

		childStage.show();

		double parentX = parent.getX();
		double parentY = parent.getY();
		double parentWidth = parent.getWidth();
		double parentHeight = parent.getHeight();

		double childWidth = childStage.getWidth();
		double childHeight = childStage.getHeight();

		double childX = parentX + (parentWidth - childWidth) / 2;
		double childY = parentY + (parentHeight - childHeight) / 2;

		childStage.setX(childX);
		childStage.setY(childY);

		childStage.requestFocus();

		childStage.toFront();
	}

	public static EventHandler<ActionEvent> getCloseHandler(final Stage stage)
	{
		return new EventHandler<ActionEvent>()
		{
			@Override
			public void handle(ActionEvent e)
			{
				StageManager.closeStage(stage);
			}
		};
	}

	public static void closeStage(Stage stage)
	{
		List<Stage> childrenOfParent = openStages.get(stage);

		if (childrenOfParent != null)
		{
			for (Stage child : childrenOfParent)
			{
				closeStage(child);
			}
		}

		notifyStageClosedListeners(stage);

		openStages.remove(stage);

		stage.close();
	}
}