/*******************************************************************************
 * Copyright (c) 2001, 2006 IBM Corporation and others.
 * All rights reserved. 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.wst.sse.core.internal.provisional.model;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceAlreadyExists;
import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceInUse;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;

/**
 * Responsible for managing IStructuredModels.
 * 
 * This allows clients to share models, so they do not have to be re-created
 * or passed around from one client to another. Clients should internally
 * enforce that models are gotten and released from locations close to each
 * other to prevent model leaks.
 * 
 * There are three ways to get a model based on usage and responsibility: for
 * 'MODIFY', just for 'SHARED', and 'UNSHARED'. Contrary to their names, a
 * model can technically be modified, and all modifications directly affect
 * the commonly shared version of the model. It is part of the API contract,
 * however, that clients who get a model for SHARED do not modify it. The
 * significance of the 'MODIFY' model is that the client is registering their
 * interest in saving changes to the model.
 * 
 * Clients can reference this interface, but should not implement.
 * 
 * @see org.eclipse.wst.sse.core.StructuredModelManager
 * @plannedfor 1.0
 */
public interface IModelManagerProposed {

	/**
	 * AccessType is used internally as a JRE 1.4 compatible enumerated type.
	 * Not intended to be referenced by clients.
	 */
	static class AccessType {
		private String fType;

		/**
		 * default access contructor we use to create our specific constants.
		 * 
		 * @param type
		 */
		AccessType(String type) {
			super();
			fType = type;
		}

		/**
		 * For debug purposes only.
		 */
		public String toString() {
			return "Model Access Type: " + fType; //$NON-NLS-1$
		}
	}

	/**
	 * Constant to provide compile time safe parameter. <code>NOTSHARED</code>signifies
	 * the client intentially wants a model that is not shared with other
	 * clients. NOTSHARED models can not be saved.
	 */
	final AccessType NOTSHARED = new AccessType("NOTSHARED"); //$NON-NLS-1$

	/**
	 * Constant to provide compile-time safe parameter. <code>SHARED</code>signifies
	 * the client is not intending to make changes and does not care whether
	 * the model accessed is saved.
	 */
	final AccessType SHARED = new AccessType("SHARED"); //$NON-NLS-1$

	/**
	 * Constant to provide compile-time safe parameter. <code>MODIFY</code>signifies
	 * the client is intending to make changes and is responsible for saving
	 * changes (or not) if they are the last one holding MODIFY access to the
	 * model before it's released.
	 */
	final AccessType MODIFY = new AccessType("MODIFY"); //$NON-NLS-1$

	/**
	 * copyModel is similar to a deep clone. The resulting model is shared,
	 * according to the value of ReadEditType. If a model already is being
	 * managed for 'newLocation' then a ResourceInUse exception is thrown,
	 * unless the ReadEditType is NOTSHARED, in which case the resulting model
	 * can not be saved.
	 * 
	 * @param oldLocation
	 * @param newLocation
	 * @param type
	 * @return
	 * @throws ResourceInUse
	 * 
	 * ISSUE: is this important enough to be API, or can clients solve
	 * themselves
	 */
	IStructuredModel copyModel(IPath oldLocation, IPath newLocation, AccessType type) throws ResourceInUse;

	/**
	 * createNewInstance is similar to clone, except the new instance has no
	 * text content. Note: this produces an UNSHARED model, for temporary use,
	 * that has the same characteristics as original model. If a true shared
	 * model is desired, use "copy".
	 * 
	 * ISSUE: still needed?
	 * 
	 * @param requester
	 * @param model
	 * @return
	 * @throws IOException
	 */
	public IStructuredModel createNewInstance(Object requester, IStructuredModel model) throws IOException;

	/**
	 * Factory method, since a proper IStructuredDocument must have a proper
	 * parser assigned. Note: its assume that IPath does not actually exist as
	 * a resource yet. If it does, ResourceAlreadyExists exception is thrown.
	 * If the resource does already exist, then createStructuredDocumentFor is
	 * the right API to use.
	 * 
	 * ISSUE: do we want to support this via model manager, or else where?
	 * ISSUE: do we need two of these? What's legacy use case?
	 * 
	 * @param location
	 * @param progressMonitor
	 * @return
	 * @throws ResourceAlreadyExists
	 * @throws IOException
	 * @throws CoreException
	 */
	IStructuredDocument createNewStructuredDocumentFor(IPath location, IProgressMonitor progressMonitor) throws ResourceAlreadyExists, IOException, CoreException;

	/**
	 * Factory method, since a proper IStructuredDocument must have a proper
	 * parser assigned. Note: clients should verify that the resource
	 * identified by the IPath exists before using this method. If this IFile
	 * does not exist, then createNewStructuredDocument is the correct API to
	 * use.
	 * 
	 * ISSUE: do we want to support this via model manager, or else where?
	 * ISSUE: do we need two of these? What's legacy use case?
	 * 
	 * @param location
	 * @param progressMonitor
	 * @return
	 * @throws IOException
	 * @throws CoreException
	 */
	IStructuredDocument createStructuredDocumentFor(IPath location, IProgressMonitor progressMonitor) throws IOException, CoreException;

	/**
	 * Note: callers of this method must still release the model when
	 * finished. Returns the model for this document if it already exists and
	 * is being shared. Returns null if this is not the case. The ReadEditType
	 * must be either MODIFY or SHARED.
	 * 
	 * ISSUE: should we accept IDocument on parameter for future evolution,
	 * and constrain to StructuredDocuments at runtime?
	 * 
	 * @param requester
	 * @param type
	 * @param document
	 * @return
	 */
	IStructuredModel getExistingModel(Object requester, AccessType type, IDocument document);

	/**
	 * Callers of this method must still release the model when finished.
	 * Returns the model for this location if it already exists and is being
	 * shared. Returns null if this is not the case. The ReadEditType must be
	 * either MODIFY or SHARED.
	 * 
	 * @param requester
	 * @param type
	 * @param location
	 * @return
	 */
	public IStructuredModel getExistingModel(Object requester, AccessType type, IPath location);

	/**
	 * Returns the model that has the specified document as its structured
	 * document.
	 * 
	 * @param requester
	 * @param type
	 * @param progressMonitor
	 * @param document
	 * @return
	 */
	public IStructuredModel getModel(Object requester, AccessType type, IProgressMonitor progressMonitor, IDocument document);

	/**
	 * Returns the model based on the content at the specified location.
	 * 
	 * Note: if the ModelManager does not know how to create a model for 
	 * such a file for content, null is return.
	 * ISSUE: should we throw some special, meaningful, checked 
	 * exception instead?
	 * 
	 * @param requester
	 * @param type
	 * @param progressMonitor
	 * @param location
	 * @return
	 * @throws IOException
	 * @throws CoreException
	 */
	public IStructuredModel getModel(Object requester, AccessType type, IProgressMonitor progressMonitor, IPath location) throws IOException, CoreException;

	/**
	 * This method will not create a new model if it already exists ... if
	 * force is false. The idea is that a client should call this method once
	 * with force set to false. If the exception is thrown, then prompt client
	 * if they want to overwrite.
	 * 
	 * @param requester
	 * @param location
	 * @param force
	 * @param type
	 * @param progressMonitor
	 * @return
	 * @throws ResourceAlreadyExists
	 * @throws ResourceInUse
	 * @throws IOException
	 * @throws CoreException
	 */
	IStructuredModel getNewModel(Object requester, IPath location, boolean force, AccessType type, IProgressMonitor progressMonitor) throws ResourceAlreadyExists, ResourceInUse, IOException, CoreException;

	/**
	 * This function returns true if there are other references to the
	 * underlying model.
	 * 
	 * @param location
	 * @return
	 */
	boolean isShared(IPath location);

	/**
	 * This function returns true if there are other references to the
	 * underlying model, shared in the specified way. The ReadEditType must be
	 * either MODIFY or SHARED.
	 * 
	 * @param location
	 * @param type
	 * @return
	 */
	boolean isShared(IPath location, AccessType type);

	/**
	 * This method can be called when the content type of a model changes. Its
	 * assumed the contentType has already been changed, and this method uses
	 * the text of the old one, to repopulate the text of the new one.
	 * 
	 * @param model
	 * @return
	 * @throws IOException
	 */
	IStructuredModel reinitialize(IStructuredModel model) throws IOException;


	/**
	 * This is used by clients to signify that they are finished with a model
	 * and will no longer access it or any of its underlying data (such as its
	 * structured document). The ReadEditType must match what ever the client
	 * used in the corresponding 'get' method.
	 * 
	 * This method must be called for every 'get'. Clients should use the
	 * try/finally pattern to ensure the release is called even if there is an
	 * unexpected exception.
	 * 
	 * @param requester
	 * @param structuredModel
	 * @param type
	 */
	void releaseModel(Object requester, IStructuredModel structuredModel, AccessType type);

	/**
	 * Writes the underlying document to the IPath.
	 * 
	 * ISSUE: we want to just "dump" contents to location, but need to spec.
	 * all the different cases of shared, etc.
	 * 
	 * ?If to same location as 'get', then same instance of model, If to
	 * different location (esp. if shared) then need to leave old instance of
	 * model, create new model, and save to new location. ?
	 * 
	 * Cases: IPath is null, write to IPath created with IPath specificed and
	 * equals IPath created with, write to IPath IPath specified and not
	 * equals IPath created with, dumps to new IPath, no change in current
	 * model state.
	 * 
	 * ISSUE: think through 'normalization' cases
	 * 
	 * 
	 * @param structuredModel
	 * @param location - already normalized?
	 * @param progressMonitor
	 * @throws UnsupportedEncodingException
	 * @throws IOException
	 * @throws CoreException
	 * 
	 * ISSUE: resource aleady exists? veto override
	 */
	void saveModel(IStructuredModel structuredModel, IPath location, IProgressMonitor progressMonitor) throws UnsupportedEncodingException, IOException, CoreException;

	/**
	 * Writes the underlying document to the IPath if the model is only shared
	 * for EDIT by one client. This is the recommended way for 'background
	 * jobs' to save models, in case the model is being shared by an editor,
	 * or other client that might desire user intervention to save a resource.
	 * 
	 * @param structuredModel
	 * @param location - already normalized?
	 * @param progressMonitor
	 * @throws UnsupportedEncodingException
	 * @throws IOException
	 * @throws CoreException
	 * 
	 * ISSUE: is locaiton needed in this case, or just use the one it was
	 * created with
	 */
	void saveModelIfNotShared(IStructuredModel structuredModel, IPath location, IProgressMonitor progressMonitor) throws UnsupportedEncodingException, IOException, CoreException;
}
