/****************************************************************************
 *  This software is part of the gCube Project.
 *  Site: http://www.gcube-system.org/
 ****************************************************************************
 * The gCube/gCore software is licensed as Free Open Source software
 * conveying to the EUPL (http://ec.europa.eu/idabc/eupl).
 * The software and documentation is provided by its authors/distributors
 * "as is" and no expressed or
 * implied warranty is given for its use, quality or fitness for a
 * particular case.
 ****************************************************************************
 * Filename: ResourceManager.java
 ****************************************************************************
 * @author <a href="mailto:daniele.strollo@isti.cnr.it">Daniele Strollo</a>
 ***************************************************************************/

package org.gcube.resourcemanagement.support.server.managers.resources;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import org.apache.axis.message.addressing.EndpointReferenceType;
import org.gcube.common.core.contexts.GCUBERemotePortTypeContext;
import org.gcube.common.core.contexts.GHNContext;
import org.gcube.common.core.informationsystem.client.AtomicCondition;
import org.gcube.common.core.informationsystem.client.ISClient;
import org.gcube.common.core.informationsystem.client.XMLResult;
import org.gcube.common.core.informationsystem.client.queries.GCUBEGenericQuery;
import org.gcube.common.core.informationsystem.client.queries.GCUBERIQuery;
import org.gcube.common.core.informationsystem.publisher.ISPublisher;
import org.gcube.common.core.resources.GCUBEResource;
import org.gcube.common.core.resources.GCUBERunningInstance;
import org.gcube.common.core.scope.GCUBEScope;
import org.gcube.common.core.scope.GCUBEScope.Type;
import org.gcube.common.core.security.GCUBESecurityManagerImpl;
import org.gcube.resourcemanagement.support.server.exceptions.AbstractResourceException;
import org.gcube.resourcemanagement.support.server.exceptions.ResourceAccessException;
import org.gcube.resourcemanagement.support.server.exceptions.ResourceOperationException;
import org.gcube.resourcemanagement.support.server.exceptions.ResourceParameterException;
import org.gcube.resourcemanagement.support.server.managers.report.ReportBuilder;
import org.gcube.resourcemanagement.support.server.managers.report.ReportEntry;
import org.gcube.resourcemanagement.support.server.managers.report.ReportOperation;
import org.gcube.resourcemanagement.support.server.managers.scope.ScopeManager;
import org.gcube.resourcemanagement.support.server.types.AllowedResourceTypes;
import org.gcube.resourcemanagement.support.server.utils.Assertion;
import org.gcube.resourcemanagement.support.server.utils.ServerConsole;

import org.gcube.vremanagement.resourcemanager.stubs.binder.AddResourcesParameters;
import org.gcube.vremanagement.resourcemanager.stubs.binder.RemoveResourcesParameters;
import org.gcube.vremanagement.resourcemanager.stubs.binder.ResourceBinderPortType;
import org.gcube.vremanagement.resourcemanager.stubs.binder.ResourceItem;
import org.gcube.vremanagement.resourcemanager.stubs.binder.ResourceList;
import org.gcube.vremanagement.resourcemanager.stubs.binder.service.ResourceBinderServiceAddressingLocator;
import org.gcube.vremanagement.resourcemanager.stubs.reporting.ReportingPortType;
import org.gcube.vremanagement.resourcemanager.stubs.reporting.service.ReportingServiceAddressingLocator;


/**
 * The minimal interface all the resource managers must implement.
 * Here is implemented the greatest part of the operations exposed in the
 * <a href="https://gcube.wiki.gcube-system.org/gcube/index.php/Programmatic_Administration_Interface">
 * official wiki</a>.
 * @author Daniele Strollo (ISTI-CNR)
 */
public abstract class AbstractResourceManager {
	private String id = null;
	private String name = null;
	private AllowedResourceTypes type = null;
	private String subType = null;
	private ISClient client = null;
	private GCUBESecurityManagerImpl managerSec = null;
	private ISPublisher publisher = null;
	private static final String LOG_PREFIX = "[AbstractResMgr]";

	/**
	 * @deprecated discouraged use. With no ID some operations cannot be accessed. For internal use only.
	 */
	public AbstractResourceManager(final AllowedResourceTypes type)
	throws ResourceParameterException, ResourceAccessException {
		Assertion<ResourceParameterException> checker = new Assertion<ResourceParameterException>();
		checker.validate(type != null, new ResourceParameterException("Invalid Parameter type"));

		this.type = type;

		/**
		 * Initially the security management is disabled.
		 */
		this.managerSec = new GCUBESecurityManagerImpl() {
			public boolean isSecurityEnabled() {
				return false;
			}
		};

		try {
			client = GHNContext.getImplementation(ISClient.class);
		} catch (Exception e) {
			throw new ResourceAccessException("Cannot instantiate the ISClient");
		}

		try {
			this.publisher = GHNContext.getImplementation(ISPublisher.class);
		} catch (Exception e) {
			ServerConsole.error(LOG_PREFIX, e);
		}
	}

	/**
	 * Instantiate the handler of a resource given its identifier and its type.
	 * Notice that this constructor is implicitly invoked when instantiating the
	 * concrete resource manager (e.g. new GHNManager(id)).
	 * @param id the identifier of the resource the manage
	 * @param type the type (GHN, RI, ...).
	 * @throws ResourceParameterException
	 * @throws ResourceAccessException
	 */
	public AbstractResourceManager(
			final String id,
			final AllowedResourceTypes type)
	throws ResourceParameterException, ResourceAccessException {
		this(type);

		Assertion<ResourceParameterException> checker = new Assertion<ResourceParameterException>();
		checker.validate(id != null && id.trim().length() > 0, new ResourceParameterException("Invalid Parameter id"));

		this.id = id.trim();
	}

	public AbstractResourceManager(
			final String id,
			final String name,
			final AllowedResourceTypes type) throws ResourceParameterException, ResourceAccessException {
		this(id, type);
		Assertion<ResourceParameterException> checker = new Assertion<ResourceParameterException>();
		checker.validate(name != null && name.trim().length() > 0, new ResourceParameterException("Invalid Parameter name"));
		this.name = name;
	}

	public AbstractResourceManager(
			final String id,
			final String name,
			final AllowedResourceTypes type,
			final String subtype)
	throws ResourceParameterException, ResourceAccessException {
		this(id, name, type);
		if (subtype != null) {
			this.subType = subtype.trim();
		}
	}

	/**
	 * The singleton ISClient instance is preferred.
	 * All resource managers can internally access this instance
	 * to make queries to the IS.
	 * @return
	 */
	protected final ISClient getISClient() {
		return this.client;
	}

	/**
	 * The singleton ISPublisher instance is preferred.
	 * All resource managers can internally access this instance
	 * to interact with ISPublisher to handle resources.
	 * @return
	 */
	public final ISPublisher getISPublisher() {
		return publisher;
	}

	public final void setSecurityManager(final GCUBESecurityManagerImpl securityManager) {
		this.managerSec = securityManager;
	}

	/**
	 * The security manager is initially instantiated with empty permissions.
	 * The {@link AbstractResourceManager#setSecurityManager} can be used to
	 * change it.
	 * @return
	 */
	public final GCUBESecurityManagerImpl getSecurityManager() {
		return this.managerSec;
	}

	/**
	 * All resources must be identifiable through an unique ID.
	 * <br/>
	 * <b>This field is mandatory</b>
	 * @return
	 */
	public final String getID() {
		return this.id;
	}

	public final void setID(final String id) {
		this.id = id;
	}

	/**
	 * All resources must have a valid name.
	 * </br>
	 * <b>This field is mandatory</b>
	 * @return
	 */
	public final String getName() {
		return this.name;
	}

	/**
	 * All resources have a type.
	 * </br>
	 * <b>This field is mandatory</b>
	 * @return
	 */
	public final AllowedResourceTypes getType() {
		return this.type;
	}

	/**
	 * Resources can have a subtype (e.g. for GHNs is the domain).
	 * </br>
	 * <b>This field is optional</b>
	 * @return
	 */
	public final String getSubType() {
		return this.subType;
	}

	/**
	 * Internally used by {@link AbstractResourceManager#getResourceManager(GCUBEScope)}.
	 * @param scope
	 * @return a raw list of resource manager descriptors.
	 * @throws Exception
	 */
	private List<GCUBERunningInstance> getResourceManagerFromScope(final GCUBEScope scope)	throws Exception {
		System.out.println("Looking for ResourceManager in scope: " + scope);
		GCUBERIQuery query = this.client.getQuery(GCUBERIQuery.class);
		query.addAtomicConditions(new AtomicCondition("//Profile/ServiceClass", "VREManagement"));
		query.addAtomicConditions(new AtomicCondition("//Profile/ServiceName", "ResourceManager"));

		List<GCUBERunningInstance> result = client.execute(query, scope);
		List<GCUBERunningInstance> toReturn = new ArrayList<GCUBERunningInstance>();
		for (GCUBERunningInstance ri : result) {
			if (ri.getScopes().containsValue(scope)) {
				toReturn.add(ri);
			}
		}
		System.out.println("Found " + toReturn.size() +" ResourceManager in scope: " + scope);
		return toReturn;
	}

	/**
	 * The the list of resource managers able to handle the resource in a given scope.
	 * @param scope the scope in which to operate
	 * @return all the available managers
	 * @throws ResourceAccessException if no manager can be instantiated
	 * @throws ResourceParameterException if the parameters are invalid
	 */
	public final List<ResourceBinderPortType> getResourceManagers(final GCUBEScope scope) throws ResourceAccessException, ResourceParameterException {
		Assertion<ResourceParameterException> checker = new Assertion<ResourceParameterException>();
		checker.validate(scope != null, new ResourceParameterException("Invalid scope"));

		
		List<GCUBERunningInstance> resourceManagerList = null;
		try {
			resourceManagerList = this.getResourceManagerFromScope(scope);
		} catch (Exception e) {
			e.printStackTrace();
			throw new ResourceAccessException("Cannot retrieve the managers for resource: " + this.getID());
		}
		List<ResourceBinderPortType> retval = new Vector<ResourceBinderPortType>();

		if (resourceManagerList.isEmpty()) {
			System.out.println("\n\n\n\n\n******   Schianta qui size: " + resourceManagerList.size() +  " scope: " + scope);
			throw new ResourceAccessException("Unable to find ResourceManagers for resource " + this.getType() + " in scope: " + scope.toString());
		}

		EndpointReferenceType endpoint = null;
		ResourceBinderPortType pt = null;

		for (GCUBERunningInstance resourceManager : resourceManagerList) {
			try {
				endpoint = resourceManager.getAccessPoint().getEndpoint("gcube/vremanagement/resourcemanager/binder");

				pt = GCUBERemotePortTypeContext.getProxy(
						new ResourceBinderServiceAddressingLocator()
						.getResourceBinderPortTypePort(endpoint),
						scope,
						this.managerSec);
				if (pt != null) {
					retval.add(pt);
				}
			} catch (Throwable e) {
				// trying on next entry
				ServerConsole.error(LOG_PREFIX, e);
			}
		}
		if (retval != null && retval.size() > 0) {
			// Return a random manager from the available ones
			return retval;
		}
		// no managers found
		throw new ResourceAccessException("Unable to find ResourceManagers for resource " + this.getID() + "in scope: " + scope.toString());
	}
	
	/**
	 * The the list of resource report managers able to handle the resource in a given scope.
	 * @param scope the scope in which to operate
	 * @return all the available managers
	 * @throws ResourceAccessException if no manager can be instantiated
	 * @throws ResourceParameterException if the parameters are invalid
	 */
	public final List<ReportingPortType> getResourceReportManagers(final GCUBEScope scope) throws ResourceAccessException, ResourceParameterException {
		Assertion<ResourceParameterException> checker = new Assertion<ResourceParameterException>();
		checker.validate(scope != null, new ResourceParameterException("Invalid scope"));

		List<GCUBERunningInstance> resourceManagerList = null;
		try {
			resourceManagerList = this.getResourceManagerFromScope(scope);
		} catch (Exception e) {
			e.printStackTrace();
			throw new ResourceAccessException("Cannot retrieve the managers for resource: " + this.getID());
		}
		List<ReportingPortType> retval = new Vector<ReportingPortType>();

		if (resourceManagerList.isEmpty()) {			
			throw new ResourceAccessException("Unable to find ResourceManagers for resource " + this.getType() + " in scope: " + scope.toString());
		}

		EndpointReferenceType endpoint = null;
		ReportingPortType pt = null;

		for (GCUBERunningInstance resourceManager : resourceManagerList) {
			try {
				endpoint = resourceManager.getAccessPoint().getEndpoint("gcube/vremanagement/resourcemanager/reporting");

				pt = GCUBERemotePortTypeContext.getProxy(
						new ReportingServiceAddressingLocator().getReportingPortTypePort(endpoint),
						scope,
						this.managerSec);
				if (pt != null) {
					retval.add(pt);
				}
			} catch (Throwable e) {
				// trying on next entry
				ServerConsole.error(LOG_PREFIX, e);
			}
		}
		if (retval != null && retval.size() > 0) {
			// Return a random report manager from the available ones
			return retval;
		}
		// no managers found
		throw new ResourceAccessException("Unable to find ReportResourceManagers for resource " + this.getID() + "in scope: " + scope.toString());
	}

	/**
	 * The resource manager needed to handle the resource in a given scope.
	 * @param scope the scope in which to operate
	 * @return a random chosen manager from the avaiable ones
	 * @throws ResourceAccessException if no manager can be instantiated
	 * @throws ResourceParameterException if the parameters are invalid
	 */
	public final ResourceBinderPortType getResourceManager(final GCUBEScope scope)
	throws AbstractResourceException {
		ServerConsole.info(LOG_PREFIX, "Getting Resource Manager in scope [" + scope.toString() + "]");

		List<ResourceBinderPortType> retval = this.getResourceManagers(scope);
		if (retval != null && retval.size() > 0) {
			Random generator = new Random();
			// Return a random manager from the available ones
			ResourceBinderPortType manager = retval.get(generator.nextInt(retval.size()));
			return manager;
		}
		// no managers found
		throw new ResourceAccessException("Unable to find ResourceManagers for resource " + this.getType() + " in scope: " + scope.toString());
	}
	
	/**
	 * The report resource manager needed to handle the resource in a given scope.
	 * @param scope the scope in which to operate
	 * @return a random chosen manager from the avaiable ones
	 * @throws ResourceAccessException if no manager can be instantiated
	 * @throws ResourceParameterException if the parameters are invalid
	 */
	public final ReportingPortType getReportResourceManager(final GCUBEScope scope)
	throws AbstractResourceException {
		ServerConsole.info(LOG_PREFIX, "Getting Resource Manager in scope [" + scope.toString() + "]");

		List<ReportingPortType> retval = this.getResourceReportManagers(scope);
		if (retval != null && retval.size() > 0) {
			Random generator = new Random();
			// Return a random manager from the available ones
			ReportingPortType manager = retval.get(generator.nextInt(retval.size()));
			return manager;
		}
		// no managers found
		throw new ResourceAccessException("Unable to find ResourceManagers for resource " + this.getType() + " in scope: " + scope.toString());
	}

	/**
	 * Once the conditions for binding a resource to a target scope are satisfied
	 * (see the
	 * <a href="https://gcube.wiki.gcube-system.org/gcube/index.php/Programmatic_Administration_Interface">
	 * official wiki</a>
	 * for scope binding rules) this method is internally called.
	 * @param targetScope
	 * @return
	 * @throws ResourceOperationException
	 */
	private String bindToScope(final GCUBEScope targetScope) throws AbstractResourceException {
		Assertion<AbstractResourceException> checker = new Assertion<AbstractResourceException>();
		checker.validate(targetScope != null, new ResourceParameterException("Invalid parameter targetScope. null not allowed."));
		checker.validate(this.getID() != null, new ResourceOperationException("Invalid resource ID. null not allowed."));

		ServerConsole.trace(
				LOG_PREFIX,
				"[BIND-SCOPE-ENTER] Adding " + this.getType() + " " + this.getID() + " to scope [" +
				targetScope.toString() + "]");

		AddResourcesParameters addParam = new AddResourcesParameters();
		ResourceItem toAdd = new ResourceItem();
		toAdd.setID(this.getID());
		toAdd.setType(this.getType().name());
		ResourceList r = new ResourceList();
		
		
		r.setResource(new ResourceItem[]{toAdd});
		addParam.setResources(r);
		addParam.setTargetScope(targetScope.toString());

		ResourceBinderPortType manager = this.getResourceManager(targetScope);
		
		try {
			String reportID =  manager.addResources(addParam);

			ServerConsole.trace(
					LOG_PREFIX,
					"[BIND-SCOPE-EXIT] Applyed Adding " + this.getType() + " " + this.getID() + " to scope [" +
					targetScope.toString() + "]... reportID: " + reportID);
			
			ReportingPortType pt = this.getReportResourceManager(targetScope);
			return pt.getReport(reportID);
		} catch (Exception e) {
			e.printStackTrace();
			ServerConsole.trace(
					LOG_PREFIX,
			"[BIND-SCOPE-EXIT] [FAILURE]");
			throw new ResourceOperationException("During resource::addToScope: " + e.getMessage());
		}
	}



	/**
	 * Add a scope to a gHN and the related Service Map is already available on the gHN.
	 * @param nestingPublication true for resources different from gHN and RI.
	 * @return the reportID generated
	 */
	public final String addToExistingScope(
			final GCUBEScope sourceScope, final GCUBEScope targetScope)
	throws AbstractResourceException {
		Assertion<AbstractResourceException> checker = new Assertion<AbstractResourceException>();
		checker.validate(sourceScope != null, new ResourceParameterException("Invalid parameter sourceScope. null not allowed."));
		checker.validate(targetScope != null, new ResourceParameterException("Invalid parameter targetScope. null not allowed."));
		checker.validate(this.getID() != null, new ResourceOperationException("Invalid resource ID. null not allowed."));

		ReportBuilder report = new ReportBuilder();

		ServerConsole.trace(
				LOG_PREFIX,
				"[ADD-ToExistingScope] Adding from scope [" +
				sourceScope.toString() +
				"] to existing scope [" +
				targetScope.toString() +
				"] " + this.getType() + " " + this.getID());

		// If not RI or GHN and the scopes are sibling and VO copyFromToVO
		if (!(this.getType() == AllowedResourceTypes.GHN) &&
				!(this.getType() == AllowedResourceTypes.RunningInstance) &&
				sourceScope.getType() == Type.VO && targetScope.getType() == Type.VO) {
			return copyFromToVO(sourceScope, targetScope);
		}

		// Add a gCube Resource to
		// (i) a VRE scope from the parent VO or
		// (ii) a VO scope from the infrastructure scope
		if (!targetScope.isEnclosedIn(sourceScope)) {
			throw new ResourceOperationException(
			"You are not allowed to apply to this scope. Target scope is not enclosed in the source one.");
		}

		report.addEntry(new ReportEntry(ReportOperation.AddToScope, this,
				"Added " + this.getType() + " " + this.getID() + " to parent scope " +
				targetScope.toString() + " the remote report ID is: " +
				this.bindToScope(targetScope), true));

		return report.getXML();
	}

	/**
	 * Similar to the {@link AbstractResourceManager#addToExistingScope} method but involves
	 * two scopes of type VO.
	 * Notice that this operation in reserved for resources different from gHN and RI.
	 * See
	 * <a href="https://gcube.wiki.gcube-system.org/gcube/index.php/Programmatic_Administration_Interface#Add_a_gCube_Resource_from_a_VO_to_another_VO">
	 * here</a> for further details.
	 * @param sourceScope
	 * @param targetScope
	 * @return
	 * @throws AbstractResourceException
	 */
	public final String copyFromToVO(final GCUBEScope sourceScope, final GCUBEScope targetScope)
	throws AbstractResourceException {
		Assertion<AbstractResourceException> checker = new Assertion<AbstractResourceException>();
		checker.validate(
				sourceScope != null && sourceScope.getType() == Type.VO,
				new ResourceParameterException("The sourceScope is invalid or not of type VO."));
		checker.validate(
				targetScope != null && targetScope.getType() == Type.VO,
				new ResourceParameterException("The targetScope is invalid or not of type VO."));
		checker.validate(
				sourceScope.getEnclosingScope() == targetScope.getEnclosingScope(),
				new ResourceParameterException("The sourceScope and targetScope must be children of the same root VO."));
		checker.validate(this.getType() != AllowedResourceTypes.GHN && this.getType() != AllowedResourceTypes.RunningInstance,
				new ResourceAccessException("Operation not allowed for RI and GHNs."));
		checker.validate(this.getID() != null,
				new ResourceAccessException("Operation not allowed on resources with no ID."));

		// Phase 1. retrieve the resource to copy
		GCUBEResource resStub = this.getGCUBEResource(sourceScope);

		// Phase 2. Before to register the resource, the scope must be
		// bound to the local GCUBEResource
		this.bindToScope(targetScope);

		// Phase 3. Register to the new VO through the ISPublisher
		try {
			return this.getISPublisher().registerGCUBEResource(resStub, targetScope, this.getSecurityManager());
		} catch (Exception e) {
			throw new ResourceAccessException(e.getMessage());
		}
	}

	/**
	 * Given the XML profile representation of a gcube resource, its GCUBEResource is built.
	 * Since it depends on the type of the resource, each concrete implementation of resource
	 * managers must implement it.
	 * @param xmlRepresentation
	 * @return
	 * @throws AbstractResourceException
	 */
	protected abstract GCUBEResource buildGCUBEResource(final String xmlRepresentation) throws AbstractResourceException;

	/**
	 * From a resource retrieves its GCUBEResource depending on the scope in which it is
	 * published.
	 * @param scope
	 * @return
	 * @throws AbstractResourceException
	 */
	public final GCUBEResource getGCUBEResource(final GCUBEScope scope) throws AbstractResourceException {
		return this.buildGCUBEResource(this.getXMLDescription(scope));
	}

	/**
	 * Returns the XML profile of a resource (given its ID that is encapsulated inside the resource
	 * manager).
	 * @param scope
	 * @return
	 * @throws AbstractResourceException
	 */
	protected final String getXMLDescription(final GCUBEScope scope) throws AbstractResourceException {
		Assertion<AbstractResourceException> checker = new Assertion<AbstractResourceException>();
		checker.validate(this.getID() != null, new ResourceAccessException("Cannot execute on resources with no ID."));

		// Phase 1. retrieve the resource to copy
		GCUBEGenericQuery query = null;
		try {
			query = this.getISClient().getQuery(GCUBEGenericQuery.class);
			query.setExpression(
					"for  $resource in  collection('/db/Profiles/" + this.getType().name() + "')//Resource " +
					"where ( $resource/ID/string() eq '" +
					this.getID() +
					"') " +
					"return $resource"
			);
		} catch (Exception e) {
			throw new ResourceAccessException(e);
		}

		XMLResult resDescription = null;
		try {
			resDescription = this.getISClient().execute(query, scope).get(0);
			return resDescription.toString();
		} catch (Exception e) {
			throw new ResourceAccessException("Cannot retrieve the IS profile for resource: " + this.getID() +
					" in scope: " + scope.toString());
		}
	}

	/**
	 * The first phase of remove form scope.
	 * This is common to all the resources (RI and GHNs).
	 * @param scope
	 * @return
	 * @throws AbstractResourceException
	 */
	private String basicRemoveFromScope(final GCUBEScope scope)
	throws AbstractResourceException {
		Assertion<AbstractResourceException> checker = new Assertion<AbstractResourceException>();
		checker.validate(scope != null, new ResourceParameterException("Invalid parameter scope. null not allowed."));
		checker.validate(this.getID() != null, new ResourceOperationException("Invalid ID. null not allowed."));

		ServerConsole.trace(LOG_PREFIX, "[REMOVE-FROM-SCOPE] Removing from scope [" + scope.toString() + "] " + this.getType() + " " + this.getID());

		String retval = null;

		RemoveResourcesParameters params = new RemoveResourcesParameters();
		ResourceItem toRemove = new ResourceItem();
		toRemove.setID(this.getID());
		toRemove.setType(this.getType().name());

		ResourceList resourcesToRemove = new ResourceList();
		resourcesToRemove.setResource(new ResourceItem[]{toRemove});
		params.setResources(resourcesToRemove);
		params.setTargetScope(scope.toString());
		ServerConsole.trace(LOG_PREFIX, "[REMOVE-FROM-SCOPE] Sending the Remove Resource request....");
		try {
			ResourceBinderPortType manager = this.getResourceManager(scope);
			retval = manager.removeResources(params);
		} catch (Exception e) {
			throw new ResourceOperationException("During removeFrom scope of "
					+ this.getType()
					+ " " + this.getID() + ": " + e.getMessage());
		}
		return retval;
	}

	/**
	 * Removes the current resource from the scope.
	 * @param nestingRemoval true for resources different from gHN and RI
	 */
	public final String removeFromScope(final GCUBEScope scope)
	throws AbstractResourceException {
		Assertion<AbstractResourceException> checker = new Assertion<AbstractResourceException>();
		checker.validate(scope != null, new ResourceParameterException("Invalid parameter scope. null not allowed."));
		checker.validate(this.getID() != null, new ResourceOperationException("Invalid ID. null not allowed."));

		String retval = this.basicRemoveFromScope(scope);

		// -- PHASE 2 - optional

		// Builds the stub without the removed scope
		GCUBEResource resStub = this.getGCUBEResource(scope);
		// Refreshes the local profile
		resStub.removeScope(scope);

		/*
		 * These steps are for resources different from GHN and RI
		 */
		if (!this.getType().equals(AllowedResourceTypes.GHN.name()) &&
				!this.getType().equals(AllowedResourceTypes.RunningInstance.name())) {
			// Phase 1
			// if the resource joins only the current scope, after the removal it has also to be deleted
			try {
				List<GCUBEScope> boundedScopes = this.validateScopes(resStub.getScopes().values().toArray(new GCUBEScope[]{}));
				if (boundedScopes == null) {
					ServerConsole.warn(LOG_PREFIX, "[REMOVE-FROM-SCOPE] The resource " + this.getType() + " has no bound scopes");
					return retval;
				}

				if (boundedScopes.size() == 0) {
					ServerConsole.trace(LOG_PREFIX, "[REMOVE-FROM-SCOPE] [DELETE-BRANCH] deleting resource since no more scopes remained");
					this.getISPublisher().removeGCUBEResource(
							this.getID(), this.getType().name(), scope, this.getSecurityManager());
					return retval;
				} else {
					// requires the update of the resources in all other scopes
					for (GCUBEScope _scope : boundedScopes) {
						if (!scope.equals(_scope)) {
							try {
								ServerConsole.trace(LOG_PREFIX, "[REMOVE-FROM-SCOPE] [UPDATE-BRANCH] Updating profile in scope [" + _scope.toString() + "]");
								this.getISPublisher().updateGCUBEResource(resStub, _scope, getSecurityManager());
							} catch (Exception e) {
								ServerConsole.error(LOG_PREFIX, e);
							}
						}
					}
				}

			} catch (Exception e) {
				throw new ResourceOperationException(e);
			}
		}

		return retval;
	}

	/**
	 * Adds to scopes the required maps (if needed).
	 * Internally used to ensure that the scopes at Infrastructure or VO level
	 * have correctly setup the maps.
	 * @param scopes
	 * @return
	 */
	protected List<GCUBEScope> validateScopes(final GCUBEScope[] scopes) {
		List<GCUBEScope> retval = new Vector<GCUBEScope>();
		for (GCUBEScope scope : scopes) {
			try {
				retval.add(ScopeManager.getScope(scope.toString()));
			} catch (Exception e) {
				ServerConsole.error(LOG_PREFIX, e);
			}
		}
		return retval;
	}


	/**
	 * @deprecated you must be sure before requiring this operation... take care
	 * @param scope where the resource is bound
	 * @throws AbstractResourceException
	 */
	public final void forceDelete(final GCUBEScope scope) throws AbstractResourceException {
		ServerConsole.trace(LOG_PREFIX, "[DELETE] [DELETE-BRANCH] deleting resource since no more scopes remained");
		try {
			this.getISPublisher().removeGCUBEResource(
					this.getID(), this.getType().name(), scope, this.getSecurityManager());
		} catch (Exception e) {
			ServerConsole.error(LOG_PREFIX, e);
		}
	}

	/**
	 * Removes a resource.
	 * According to the
	 * <a href="https://gcube.wiki.gcube-system.org/gcube/index.php/Programmatic_Administration_Interface">
	 * official wiki</a> the resource is deleted only if is not bound in other scopes. Otherwise this
	 * operation simply corresponds to the in deep remove from scope.
	 * @param scope
	 * @throws ResourceOperationException
	 */
	public final void delete(final GCUBEScope scope) throws AbstractResourceException {
		Assertion<AbstractResourceException> checker = new Assertion<AbstractResourceException>();
		checker.validate(scope != null, new ResourceParameterException("Invalid parameter scope. null not allowed."));
		checker.validate(this.getID() != null, new ResourceOperationException("Invalid ID. null not allowed."));

		System.out.println("DELETING TYPE: "+ this.getType());
		
		GCUBEResource resStub = this.getGCUBEResource(scope);
		List<GCUBEScope> boundedScopes = this.validateScopes(resStub.getScopes().values().toArray(new GCUBEScope[]{}));

		ServerConsole.trace(LOG_PREFIX, "[DELETE] " + this.getType() + " " + this.getID() + " in scope [" + scope + "]");

		ServerConsole.trace(LOG_PREFIX, "[DELETE] " + this.getType() + " " + this.getID() + " is bound to (" + boundedScopes.size() + ") scopes");

		/*
		 * Removefromscope accetta un boolean piu la resource gia ottenuta.
		 * boolean per decidere se saltare fase due di update che la ripeto piu volte
		 * rispetto a qui che lo faccio una volta sola.
		 */
		for (GCUBEScope _scope : boundedScopes) {
			// Removing from the children scopes
			if (_scope.isEnclosedIn(scope) || scope.equals(_scope)) {
				this.basicRemoveFromScope(_scope);
				resStub.removeScope(_scope);
			}
		}

		// Phase 2
		// after the removal, the resource must be also updated in the other scopes it belongs to,
		// otherwise it will wrongly report there a scope where actually it is not registered anymore.
		// In order to do so, the just removed scope must be removed from the local
		// GCUBEResource object by invoking the method GCUBEResource.removeScope(<scope to remove>)
		// and then the resource must be updated in all the remaining scopes.


		if (resStub.getScopes().isEmpty()) {
			ServerConsole.trace(LOG_PREFIX, "[DELETE] [DELETE-BRANCH] deleting resource since no more scopes remained");
			try {
				this.getISPublisher().removeGCUBEResource(
						this.getID(), this.getType().name(), scope, this.getSecurityManager());
			} catch (Exception e) {

			}
		} else {
			boundedScopes = this.validateScopes(resStub.getScopes().values().toArray(new GCUBEScope[]{}));
			for (GCUBEScope _scope : boundedScopes) {
				try {
					ServerConsole.trace(LOG_PREFIX, "[DELETE] [UPDATE-BRANCH]" + this.getType() + " " + this.getID() + " in scope [" + _scope + "]");
					this.getISPublisher().updateGCUBEResource(resStub, _scope, getSecurityManager());
				} catch (Exception e) {
					ServerConsole.error(LOG_PREFIX, e);
				}
			}
		}
	}

}
