package de.codecentric.centerdevice.listener;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import de.codecentric.centerdevice.util.StageUtils;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.stage.Stage;

public class WindowMenuUpdateListener implements ListChangeListener<Stage> {

	private final WeakReference<Menu> windowMenu;
	private final Map<Stage, CheckMenuItem> createdMenuItems;
	private final ObservableList<Stage> stages;

	public WindowMenuUpdateListener(Menu windowMenu) {
		this.windowMenu = new WeakReference<>(windowMenu);
		createdMenuItems = new HashMap<>();

		addItemsToMenu(StageUtils.getStages());

		stages = FXCollections.observableArrayList(stage -> new Observable[] {stage.focusedProperty()});
		Bindings.bindContent(stages, StageUtils.getStages());

		stages.addListener((Change <? extends Stage> c) -> checkFocusedStage());
	}

	private void checkFocusedStage() {
		Optional<Stage> focusedStage = stages.stream().filter(Stage::isFocused).findFirst();
		createdMenuItems.entrySet().forEach((entry) ->
				entry.getValue().setSelected(focusedStage.isPresent() && focusedStage.get().equals(entry.getKey())));
	}

	@Override
	public void onChanged(ListChangeListener.Change<? extends Stage> c) {
		while (c.next()) {
			updateWindowMenuItems(c.getAddedSubList(), c.getRemoved());
		}
	}

	private void updateWindowMenuItems(List<? extends Stage> add, List<? extends Stage> remove) {
		removeItemsFromMenu(remove);
		addItemsToMenu(add);
	}

	private void addItemsToMenu(List<? extends Stage> add) {
		Menu menu = windowMenu.get();
		if (add != null && menu != null) {
			add.forEach(stage -> addWindowMenuItem(stage, menu));
		}
	}

	private void removeItemsFromMenu(List<? extends Stage> remove) {
		Menu menu = windowMenu.get();
		if (remove != null && menu != null) {
      remove.forEach(stage -> removeWindowMenuItem(stage, menu));
    }
	}

	private void removeWindowMenuItem(Stage stage, Menu menu) {
		MenuItem menuItem = createdMenuItems.get(stage);
		if (menuItem != null) {
      menu.getItems().remove(menuItem);
    }
	}

	private void addWindowMenuItem(Stage stage, Menu menu) {
		CheckMenuItem item = new CheckMenuItem(stage.getTitle());
		item.setOnAction(event -> stage.toFront());
		createdMenuItems.put(stage, item);
		menu.getItems().add(item);
	}

}