/***************************************************************************
 * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ***************************************************************************/

package kieker.webgui.service;

import java.io.IOException;
import java.util.List;

import kieker.analysis.AnalysisController.STATE;
import kieker.analysis.model.analysisMetaModel.MIProject;
import kieker.webgui.common.exception.AnalysisDisplayReloadException;
import kieker.webgui.common.exception.AnalysisInitializationException;
import kieker.webgui.common.exception.InvalidAnalysisStateException;
import kieker.webgui.common.exception.NewerProjectException;
import kieker.webgui.common.exception.ProjectAlreadyExistingException;
import kieker.webgui.common.exception.ProjectNotExistingException;
import kieker.webgui.common.exception.ProjectStillRunningException;
import kieker.webgui.domain.ComponentListContainer;
import kieker.webgui.domain.DisplayType;

import org.primefaces.model.UploadedFile;

import org.springframework.security.access.prepost.PreAuthorize;

/**
 * This is the interface for a component which is responsible for managing all projects. This means that this interface can be used to create, copy, delete projects
 * as well as to start and stop the corresponding analyses and similar things. The facade should abstract from details like the (memory) locations and from
 * synchronization. Latter should be guaranteed by the facade, which means that multiple requests for the same project do not go wrong.
 * 
 * @author Nils Christian Ehmke
 */
public interface IProjectService {

	/**
	 * This method adds a new project to the application. It creates an empty, but nevertheless valid kax-file to the file system. If the method fails due to an
	 * {@link IOException}, it will make sure that the project-directories will be removed as if the method would never have been called.
	 * 
	 * @param projectName
	 *            The name of the new project.
	 * @param username
	 *            The name of the user who created the project.
	 * 
	 * @throws ProjectAlreadyExistingException
	 *             If a project with the same name exists already.
	 * @throws IOException
	 *             If something went wrong during the creation of the project.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void addProject(String projectName, String username) throws ProjectAlreadyExistingException, IOException;

	/**
	 * This method removes the given project from the application. This is done by simply removing the whole project directory. It is possible that this action fails
	 * for various reasons - in this cases it is not guaranteed that the project is still in a valid state.
	 * 
	 * @param projectName
	 *            The name of the project.
	 * 
	 * @throws ProjectNotExistingException
	 *             If a project with the given name doesn't exist.
	 * @throws IOException
	 *             If something went wrong during the removing.
	 * @throws ProjectStillRunningException
	 *             If the project is still running.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void deleteProject(String projectName) throws ProjectNotExistingException, IOException, ProjectStillRunningException;

	/**
	 * Reloads the displays of the given project.
	 * 
	 * @param projectName
	 *            The name of the project.
	 * 
	 * @throws AnalysisDisplayReloadException
	 *             If something went wrong while reloading the displays.
	 * @throws InvalidAnalysisStateException
	 *             If the analysis is in an invalid state for the operation.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void reloadDisplays(String projectName) throws AnalysisDisplayReloadException, InvalidAnalysisStateException;

	/**
	 * This method imports an existing kax-file into the application. If the given project name does already exist, the application will not try to upload it in the
	 * first place.
	 * 
	 * @param projectName
	 *            The name of the new project.
	 * @param username
	 *            The name of the user who imported the project.
	 * @param file
	 *            The kax file to be uploaded.
	 * 
	 * @throws ProjectAlreadyExistingException
	 *             If a project with the same name exists already.
	 * @throws IOException
	 *             If something went wrong during the creation of the project or the uploading of the file.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void importProject(String projectName, String username, UploadedFile file) throws ProjectAlreadyExistingException, IOException;

	/**
	 * This method makes a copy of a project and saves it under another name. If the method fails due to an {@link IOException}, it will make sure that the
	 * project-directories of the destination-project will be removed as if the method would never have been called.
	 * 
	 * @param originalProjectName
	 *            The name of the source project.
	 * @param newProjectName
	 *            The name of the target project.
	 * 
	 * @throws ProjectNotExistingException
	 *             If a project with the given (source) name doesn't exist.
	 * @throws ProjectAlreadyExistingException
	 *             If a project with the same (target) name exists already.
	 * @throws IOException
	 *             If something went wrong during the creation of the target-project or during the loading of the source-project.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void copyProject(String originalProjectName, String newProjectName) throws ProjectNotExistingException, ProjectAlreadyExistingException, IOException;

	/**
	 * This method renames the given project.
	 * 
	 * @param originalProjectName
	 *            The old project name.
	 * @param newProjectName
	 *            The new project name.
	 * 
	 * @throws ProjectNotExistingException
	 *             If a project with the given (source) name doesn't exist.
	 * @throws ProjectAlreadyExistingException
	 *             If a project with the same (target) name exists already.
	 * @throws IOException
	 *             If something went wrong during the creation of the target-project or during the loading of the source-project.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void renameProject(String originalProjectName, String newProjectName) throws ProjectNotExistingException, ProjectAlreadyExistingException, IOException;

	/**
	 * This method loads the kax-file for the given project name and delivers an initializes instance of {@link MIProject}.
	 * 
	 * @param projectName
	 *            The name of the project to be loaded.
	 * @return The model instance as defined by the corresponding kax-file.
	 * @throws ProjectNotExistingException
	 *             If a project with the given (source) name doesn't exist.
	 * @throws IOException
	 *             If something went wrong during the opening of the project.
	 */
	@PreAuthorize("isAuthenticated()")
	public MIProject loadProject(String projectName) throws ProjectNotExistingException, IOException;

	/**
	 * This method tries to save the given model instance for the given project. The given time stamp will be compared (if the corresponding flag says so) with the
	 * current time stamp of the project. If the project on the file system has been modified in the meantime, a {@link NewerProjectException} will be thrown. If
	 * something goes wrong during the storage, it is <b>not</b> guaranteed that the resulting file will be valid.
	 * 
	 * @param projectName
	 *            The name of the project which has to be saved.
	 * @param project
	 *            The model instance to be stored in the corresponding kax-file.
	 * @param timeStamp
	 *            The time stamp which has to be compared with the "real" time stamp of the project.
	 * @param overwriteNewerProject
	 *            Determines whether a newer project file will be overwritten without further warning or not.
	 * @param username
	 *            The name of the user who saves the project.
	 * @param analysisLayout
	 *            The current layout of the analysis graph. It is valid if this parameter is null. In this case the stored layout will not be changed.
	 * @param cockpitLayout
	 *            The current layout of the cockpit. It is valid if this parameter is null. In this case the stored layout will not be changed.
	 * 
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 * @throws IOException
	 *             If something went wrong during the storage of the model instance.
	 * @throws NewerProjectException
	 *             If the project on the file system is newer and the overwriteNewerProject-flag has not been set.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void saveProject(String projectName, MIProject project, long timeStamp, boolean overwriteNewerProject, String username, // NOPMD (parameters)
			String analysisLayout, String cockpitLayout) throws ProjectNotExistingException, IOException, NewerProjectException;

	/**
	 * Delivers the current time stamp of the given project.
	 * 
	 * @param projectName
	 *            The name of the project whose time stamp will be delivered.
	 * @return The current time stamp.
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 */
	@PreAuthorize("isAuthenticated()")
	public long getCurrTimeStamp(String projectName) throws ProjectNotExistingException;

	/**
	 * Delivers the owner of the given project or a substituion if the meta data is corrupt or missing.
	 * 
	 * @param projectName
	 *            The name of the project whose owner should be delivered.
	 * @return The owner (creator) of the project.
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 */
	@PreAuthorize("isAuthenticated()")
	public String getOwner(String projectName) throws ProjectNotExistingException;

	/**
	 * Delivers the last user of the given project or a substituion if the meta data is corrupt or missing.
	 * 
	 * @param projectName
	 *            The name of the project whose last user should be delivered.
	 * 
	 * @return The last user of the project.
	 * 
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 */
	@PreAuthorize("isAuthenticated()")
	public String getLastUser(String projectName) throws ProjectNotExistingException;

	/**
	 * This method tries to upload a dependency to the given project. An existing version of the library will be overwritten.
	 * 
	 * @param file
	 *            The file to be uploaded to the project.
	 * @param projectName
	 *            The name of the project.
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 * @throws IOException
	 *             If something went wrong during the uploading.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void uploadLibrary(UploadedFile file, String projectName) throws ProjectNotExistingException, IOException;

	/**
	 * This method delivers a class loader containing the currently available libraries of the given project.
	 * 
	 * @param projectName
	 *            The name of the project.
	 * @param requester
	 *            The requester of the class loader.
	 * @return A class loader for the given project.
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 * @throws IOException
	 *             If something went wrong during the initialization of the class loader.
	 */
	@PreAuthorize("isAuthenticated()")
	public ClassLoader getClassLoader(String projectName, Object requester) throws ProjectNotExistingException, IOException;

	/**
	 * This method lists all available libraries of the given project.
	 * 
	 * @param projectName
	 *            The name of the project whose libraries have to be delivered.
	 * @return A list containing all available library-names of the project.
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 */
	@PreAuthorize("isAuthenticated()")
	public List<String> listAllLibraries(String projectName) throws ProjectNotExistingException;

	/**
	 * Delivers the available components (readers, filters and repositories) for the given project.
	 * 
	 * @param project
	 *            The project whose components should be loaded.
	 * @return An object containing the available components as model instances.
	 */
	@PreAuthorize("isAuthenticated()")
	public abstract ComponentListContainer getAvailableComponents(String project);

	/**
	 * This method initializes the analysis of the given project.
	 * 
	 * @param projectName
	 *            The name of the project whose analysis should be initialized.
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 * @throws InvalidAnalysisStateException
	 *             If the analysis of the given project is in the wrong state to be initialized. This means that it has not been cleaned yet.
	 * @throws AnalysisInitializationException
	 *             If an error occurred during the initialization of the analysis.
	 * @throws IOException
	 *             If something went wrong while loading the file.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void initializeAnalysis(String projectName) throws ProjectNotExistingException, InvalidAnalysisStateException, AnalysisInitializationException,
			IOException;

	/**
	 * This method cleans the analysis of the given project.
	 * 
	 * @param projectName
	 *            The name of the project whose analysis should be cleaned.
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 * @throws InvalidAnalysisStateException
	 *             If the analysis of the given project is in the wrong state to be cleaned. This means that it has not been terminated yet.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void cleanAnalysis(String projectName) throws ProjectNotExistingException, InvalidAnalysisStateException;

	/**
	 * This method starts the analysis of the given project.
	 * 
	 * @param projectName
	 *            The name of the project whose analysis should be started.
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 * @throws InvalidAnalysisStateException
	 *             If the analysis of the given project is in the wrong state to be started. This means that it has not been initialized yet.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void startAnalysis(String projectName) throws ProjectNotExistingException, InvalidAnalysisStateException;

	/**
	 * This method stops the analysis of the given project.
	 * 
	 * @param projectName
	 *            The name of the project whose analysis should be stopped.
	 * 
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 * @throws InvalidAnalysisStateException
	 *             If the analysis of the given project is in the wrong state to be stopped. This means that it has not been started yet or is no longer running.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void stopAnalysis(String projectName) throws ProjectNotExistingException, InvalidAnalysisStateException;

	/**
	 * Initializes an emergency shutdown.
	 * 
	 * @param projectName
	 *            The name of the project whose analysis should be stopped.
	 * 
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 * @throws InvalidAnalysisStateException
	 *             If the analysis of the given project is in the wrong state to be stopped.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public void emergencyShutdownOfAnalysis(String projectName) throws ProjectNotExistingException, InvalidAnalysisStateException;

	/**
	 * This method delivers the display object of the (currently running) analysis for the given project and the given parameters. Technically it is an instance of
	 * {@code AbstractDisplay}, but in fact the project specific class loader has been used.
	 * 
	 * @param projectName
	 *            The name of the project.
	 * @param viewName
	 *            The name of the view.
	 * @param displayName
	 *            The name of the display.
	 * @return A display object for the given parameters.
	 * 
	 * @throws InvalidAnalysisStateException
	 *             If the analysis of the given project is in the wrong state to deliver the display.
	 */
	@PreAuthorize("isAuthenticated()")
	public Object getDisplay(String projectName, String viewName, String displayName) throws InvalidAnalysisStateException;

	/**
	 * This method delivers the current state of the given project.
	 * 
	 * @param projectName
	 *            The name of the project whose state have to be returned.
	 * 
	 * @return The state of the given project, if available. {@code null} otherwise.
	 */
	@PreAuthorize("isAuthenticated()")
	public STATE getCurrentState(String projectName);

	/**
	 * This method delivers the available log entries of the analysis controller of the given project.
	 * 
	 * @param projectName
	 *            The name of the project.
	 * @return An array containing the entries of the log.
	 * @throws InvalidAnalysisStateException
	 *             If the analysis is in an invalid state to deliver the entries.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public Object[] getLogEntries(String projectName) throws InvalidAnalysisStateException;

	/**
	 * Deletes the given library of the given project.
	 * 
	 * @param projectName
	 *            The name of the project.
	 * @param libName
	 *            The name of the library.
	 * @return true if and only if the given library has been removed.
	 * @throws IOException
	 *             If something went wrong during the reloading of the components.
	 * @throws ProjectNotExistingException
	 *             If a project with the given name does not exist.
	 */
	@PreAuthorize("hasAnyRole('User', 'Administrator')")
	public boolean deleteLibrary(String projectName, String libName) throws IOException, ProjectNotExistingException;

	/**
	 * Delivers the stored analysis layout for the given project.
	 * 
	 * @param projectName
	 *            The name of the project.
	 * 
	 * @return The layout string as it is stored within the meta data. If this isn't available, null will be returned.
	 */
	@PreAuthorize("isAuthenticated()")
	public abstract String getAnalysisLayout(String projectName);

	/**
	 * Delivers the stored cockpit layout for the given project.
	 * 
	 * @param projectName
	 *            The name of the project.
	 * 
	 * @return The layout string as it is stored within the meta data. If this isn't available, null will be returned.
	 */
	@PreAuthorize("isAuthenticated()")
	public abstract String getCockpitLayout(String projectName);

	/**
	 * Delivers the type of the given display connector.
	 * 
	 * @param projectName
	 *            The name of the project.
	 * @param viewName
	 *            The name of the view.
	 * @param displayConnectorName
	 *            The name of the display connector.
	 * 
	 * @return The type of the display connector.
	 */
	@PreAuthorize("isAuthenticated()")
	public DisplayType getDisplayType(String projectName, String viewName, String displayConnectorName);

	/**
	 * Delivers a list of the currently available projects. The returned list is a copy.
	 * 
	 * @return A list containing the available projects.
	 */
	@PreAuthorize("isAuthenticated()")
	public List<String> getProjects();

}
