/*
 * ShootOFF - Software for Laser Dry Fire Training
 * Copyright (C) 2016 phrack
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.shootoff.gui.pane;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;

public abstract class Slide {
	// We set a maximum on the number of control buttons at the top
	// because we want them to always maintain the same alighment and
	// size. If slide had more than the maximum, it would break
	// the size and alignment invariant.
	public static final int MAX_CONTROL_BUTTONS = 4;
	public static final int CONTROL_BUTTON_WIDTH = 150;
	public static final int CONTROL_BUTTON_HEIGHT = 100;

	private final List<Node> controlNodes = new ArrayList<>();
	private final List<Node> bodyNodes = new ArrayList<>();
	private final List<Node> savedControls = new ArrayList<>();
	private final List<Node> savedBody = new ArrayList<>();

	private Optional<SlideHiddenListener> slideHiddenListener = Optional.empty();
	private final Pane parentControls;
	private final Pane parentBody;

	public Slide(Pane parentControls, Pane parentBody) {
		this.parentControls = parentControls;
		this.parentBody = parentBody;

		final ImageView backImage = new ImageView(
				new Image(Slide.class.getResourceAsStream("/images/back_button.png"), 60.0, 60.0, true, true));
		final Button backButton = addSlideControlButton("", (Event) -> hide());
		backButton.setGraphic(backImage);
	}

	public void showControls() {
		// If we have less than the maximum number of control buttons
		// add empty panes as place holders for missing buttons to
		// ensure control button alignment and size is maintained.
		if (controlNodes.size() < MAX_CONTROL_BUTTONS) {
			final int sizeDelta = MAX_CONTROL_BUTTONS - controlNodes.size();

			for (int i = 0; i < sizeDelta; i++) {
				final Pane placeHolderPane = new Pane();
				placeHolderPane.setPrefSize(CONTROL_BUTTON_WIDTH, CONTROL_BUTTON_HEIGHT);
				controlNodes.add(placeHolderPane);
			}
		}

		show(parentControls, controlNodes, savedControls);
	}

	public void showBody() {
		show(parentBody, bodyNodes, savedBody);
	}

	public void hide() {
		hide(parentBody, bodyNodes, savedBody);
		hide(parentControls, controlNodes, savedControls);

		// Assumption that body will never show without the controls
		if (slideHiddenListener.isPresent()) slideHiddenListener.get().onSlideHidden();
	}

	private void show(Pane parentContainer, List<Node> slideList, List<Node> savedList) {
		// Do not show twice
		if (!savedList.isEmpty()) return;

		savedList.addAll(parentContainer.getChildren());
		parentContainer.getChildren().setAll(slideList);
	}

	private void hide(Pane parentContainer, List<Node> slideList, List<Node> savedList) {
		parentContainer.getChildren().removeAll(slideList);
		parentContainer.getChildren().addAll(savedList);
		savedList.clear();
	}

	public void setOnSlideHidden(SlideHiddenListener slideHiddenListener) {
		this.slideHiddenListener = Optional.ofNullable(slideHiddenListener);
	}

	// This used to add the buttons that appear across the very top row
	// of the slide. These are intended to control the content
	// that appears on the rest of the slide
	protected Button addSlideControlButton(String text, final EventHandler<ActionEvent> eventHandler) {
		if (controlNodes.size() >= MAX_CONTROL_BUTTONS) {
			throw new AssertionError("The slide already has the maximum number of control buttons");
		}

		final Button controlButton = new Button(text);
		controlButton.setPrefSize(CONTROL_BUTTON_WIDTH, CONTROL_BUTTON_HEIGHT);
		controlButton.setOnAction(eventHandler);
		controlNodes.add(controlButton);

		return controlButton;
	}

	protected void addBodyNode(Node node) {
		bodyNodes.add(node);
	}
}