/*
 * Copyright (c) 2018 Karl Tauber <karl at jformdesigner dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  o Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.markdownwriterfx.projects;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.stage.DirectoryChooser;
import javafx.stage.Window;
import org.markdownwriterfx.MarkdownWriterFXApp;
import org.markdownwriterfx.Messages;
import org.markdownwriterfx.util.Utils;

/**
 * Project manager.
 *
 * @author Karl Tauber
 */
public class ProjectManager
{
	private static final String KEY_PROJECTS = "projects";
	private static final String KEY_PATH = "path";
	private static final String KEY_ACTIVE_PROJECT = "active";
	private static final String KEY_LAST_PROJECT_DIRECTORY = "lastDirectory";

	// 'activeProject' property
	private static final ObjectProperty<File> activeProject = new SimpleObjectProperty<>();
	public static File getActiveProject() { return activeProject.get(); }
	public static void setActiveProject(File activeProject) { ProjectManager.activeProject.set(activeProject); }
	public static ObjectProperty<File> activeProjectProperty() { return activeProject; }

	// 'projects' property
	private static final ObservableList<File> projects = FXCollections.observableArrayList();
	static ObservableList<File> getProjects() { return projects; }

	static {
		Preferences state = getProjectsState();

		// load recent projects
		projects.addAll(getRecentProjects());

		// save active project on change
		activeProjectProperty().addListener((observer, oldProject, newProject) -> {
			Utils.putPrefs(state, KEY_ACTIVE_PROJECT, (newProject != null) ? newProject.getAbsolutePath() : null, null);

			// add to recent projects
			if (newProject != null && !projects.contains(newProject))
				projects.add(newProject);
		});

		// save recent projects on change
		projects.addListener((ListChangeListener<File>) change -> {
			while (change.next()) {
				if (change.wasAdded()) {
					for (File f : change.getAddedSubList()) {
						getProjectState(f);
					}
				}
				if (change.wasRemoved()) {
					for (File f : change.getRemoved())
						removeProjectState(f);
				}
			}
		});

		// initialize active project
		String activeProjectName = state.get(KEY_ACTIVE_PROJECT, null);
		if (activeProjectName != null)
			setActiveProject(new File(activeProjectName));
		if (getActiveProject() == null && !projects.isEmpty())
			setActiveProject(projects.get(0));
	}

	public static void openProject(Window ownerWindow) {
		DirectoryChooser fileChooser = new DirectoryChooser();
		fileChooser.setTitle(Messages.get("ProjectManager.openChooser.title"));

		Preferences state = getProjectsState();
		String lastProjectDirectory = state.get(KEY_LAST_PROJECT_DIRECTORY, null);
		File file = new File((lastProjectDirectory != null) ? lastProjectDirectory : ".");
		if (!file.isDirectory())
			file = new File(".");
		fileChooser.setInitialDirectory(file);

		File selectedFile = fileChooser.showDialog(ownerWindow);
		if (selectedFile == null)
			return;

		state.put(KEY_LAST_PROJECT_DIRECTORY, selectedFile.getAbsolutePath());

		setActiveProject(selectedFile);
	}

	public static Preferences getActiveProjectState() {
		return (getActiveProject() != null) ? getProjectState(getActiveProject()) : null;
	}

	public static Preferences getProjectState(File project) {
		return getProjectState(project, true);
	}

	private static Preferences getProjectState(File project, boolean create) {
		Preferences state = getProjectsState();

		try {
			String[] childrenNames = state.childrenNames();
			for (String childName : childrenNames) {
				Preferences child = state.node(childName);
				String path = child.get(KEY_PATH, null);
				if (path != null && project.equals(new File(path)))
					return child;
			}

			if (!create)
				return null;

			int lastID = 0;
			for (String childName : childrenNames) {
				try {
					int childID = Integer.parseInt(childName);
					if (childID > lastID)
						lastID = childID;
				} catch (NumberFormatException ex) {
					// ignore
				}
			}

			Preferences newNode = state.node(String.valueOf(lastID + 1));
			newNode.put(KEY_PATH, project.getAbsolutePath());
			return newNode;
		} catch (BackingStoreException ex) {
			// ignore
			ex.printStackTrace();
			return null;
		}
	}

	private static void removeProjectState(File project) {
		Preferences projectState = getProjectState(project, false);
		if (projectState != null) {
			try {
				projectState.removeNode();
			} catch (BackingStoreException ex) {
				// ignore
				ex.printStackTrace();
			}
		}
	}

	private static List<File> getRecentProjects() {
		Preferences state = getProjectsState();
		ArrayList<File> projects = new ArrayList<>();

		try {
			String[] childrenNames = state.childrenNames();
			for (String childName : childrenNames) {
				Preferences child = state.node(childName);
				String path = child.get(KEY_PATH, null);
				if (path != null)
					projects.add(new File(path));
			}
		} catch (BackingStoreException ex) {
			// ignore
			ex.printStackTrace();
		}

		return projects;
	}

	private static Preferences getProjectsState() {
		return MarkdownWriterFXApp.getState().node(KEY_PROJECTS);
	}
}
