/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.ui.internal;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IElementFactory;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPersistableElement;
import org.eclipse.ui.PlatformUI;

/**
 * An item in the editor history.
 */
public class EditorHistoryItem {

	private IEditorInput input;

	private IEditorDescriptor descriptor;

	private IMemento memento;

	/**
	 * Constructs a new item.
	 */
	public EditorHistoryItem(IEditorInput input, IEditorDescriptor descriptor) {
		this.input = input;
		this.descriptor = descriptor;
	}

	/**
	 * Constructs a new item from a memento.
	 */
	public EditorHistoryItem(IMemento memento) {
		this.memento = memento;
	}

	/**
	 * Returns the editor descriptor.
	 *
	 * @return the editor descriptor.
	 */
	public IEditorDescriptor getDescriptor() {
		return descriptor;
	}

	/**
	 * Returns the editor input.
	 *
	 * @return the editor input.
	 */
	public IEditorInput getInput() {
		return input;
	}

	/**
	 * Returns whether this item has been restored from the memento.
	 */
	public boolean isRestored() {
		return memento == null;
	}

	/**
	 * Returns the name of this item, either from the input if restored, otherwise
	 * from the memento.
	 */
	public String getName() {
		if (isRestored() && getInput() != null) {
			return getInput().getName();
		} else if (memento != null) {
			String name = memento.getString(IWorkbenchConstants.TAG_NAME);
			if (name != null) {
				return name;
			}
		}
		return ""; //$NON-NLS-1$
	}

	/**
	 * Returns the tooltip text of this item, either from the input if restored,
	 * otherwise from the memento.
	 */
	public String getToolTipText() {
		if (isRestored() && getInput() != null) {
			return getInput().getToolTipText();
		} else if (memento != null) {
			String name = memento.getString(IWorkbenchConstants.TAG_TOOLTIP);
			if (name != null) {
				return name;
			}
		}
		return ""; //$NON-NLS-1$
	}

	/**
	 * Returns whether this item matches the given editor input.
	 */
	public boolean matches(IEditorInput input) {
		if (isRestored()) {
			return input.equals(getInput());
		}
		// if not restored, compare name, tool tip text and factory id,
		// avoiding as much work as possible
		if (!getName().equals(input.getName())) {
			return false;
		}
		if (!getToolTipText().equals(input.getToolTipText())) {
			return false;
		}
		IPersistableElement persistable = input.getPersistable();
		String inputId = persistable == null ? null : persistable.getFactoryId();
		String myId = getFactoryId();
		return myId == null ? inputId == null : myId.equals(inputId);
	}

	/**
	 * Returns the factory id of this item, either from the input if restored,
	 * otherwise from the memento. Returns <code>null</code> if there is no factory
	 * id.
	 */
	public String getFactoryId() {
		if (isRestored()) {
			if (input != null) {
				IPersistableElement persistable = input.getPersistable();
				if (persistable != null) {
					return persistable.getFactoryId();
				}
			}
		} else if (memento != null) {
			return memento.getString(IWorkbenchConstants.TAG_FACTORY_ID);
		}
		return null;
	}

	/**
	 * Restores the object state from the memento.
	 */
	public IStatus restoreState() {
		Assert.isTrue(!isRestored());

		IStatus result = Status.OK_STATUS;
		IMemento memento = this.memento;
		this.memento = null;

		String factoryId = memento.getString(IWorkbenchConstants.TAG_FACTORY_ID);
		if (factoryId == null) {
			WorkbenchPlugin.log("Unable to restore mru list - no input factory ID.");//$NON-NLS-1$
			return result;
		}
		IElementFactory factory = PlatformUI.getWorkbench().getElementFactory(factoryId);
		if (factory == null) {
			return result;
		}
		IMemento persistableMemento = memento.getChild(IWorkbenchConstants.TAG_PERSISTABLE);
		if (persistableMemento == null) {
			WorkbenchPlugin.log("Unable to restore mru list - no input element state: " + factoryId);//$NON-NLS-1$
			return result;
		}
		IAdaptable adaptable = factory.createElement(persistableMemento);
		if (adaptable == null || (adaptable instanceof IEditorInput) == false) {
			return result;
		}
		input = (IEditorInput) adaptable;
		// Get the editor descriptor.
		String editorId = memento.getString(IWorkbenchConstants.TAG_ID);
		if (editorId != null) {
			IEditorRegistry registry = WorkbenchPlugin.getDefault().getEditorRegistry();
			descriptor = registry.findEditor(editorId);
		}
		return result;
	}

	/**
	 * Returns whether this history item can be saved.
	 */
	public boolean canSave() {
		return !isRestored() || (getInput() != null && getInput().getPersistable() != null);
	}

	/**
	 * Saves the object state in the given memento.
	 *
	 * @param memento the memento to save the object state in
	 */
	public IStatus saveState(IMemento memento) {
		if (!isRestored()) {
			memento.putMemento(this.memento);
		} else if (input != null) {

			IPersistableElement persistable = input.getPersistable();
			if (persistable != null) {
				/*
				 * Store IPersistable of the IEditorInput in a separate section since it could
				 * potentially use a tag already used in the parent memento and thus overwrite
				 * data.
				 */
				IMemento persistableMemento = memento.createChild(IWorkbenchConstants.TAG_PERSISTABLE);
				persistable.saveState(persistableMemento);
				memento.putString(IWorkbenchConstants.TAG_FACTORY_ID, persistable.getFactoryId());
				if (descriptor != null && descriptor.getId() != null) {
					memento.putString(IWorkbenchConstants.TAG_ID, descriptor.getId());
				}
				// save the name and tooltip separately so they can be restored
				// without having to instantiate the input, which can activate plugins
				memento.putString(IWorkbenchConstants.TAG_NAME, input.getName());
				memento.putString(IWorkbenchConstants.TAG_TOOLTIP, input.getToolTipText());
			}
		}
		return Status.OK_STATUS;
	}

}
