/****************************************************************************
 *  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: RunningInstanceManager.java
 ****************************************************************************
 * @author <a href="mailto:daniele.strollo@isti.cnr.it">Daniele Strollo</a>
 ***************************************************************************/

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

import java.io.StringReader;
import java.util.List;
import java.util.Vector;

import org.gcube.common.core.contexts.GHNContext;
import org.gcube.common.core.informationsystem.client.AtomicCondition;
import org.gcube.common.core.informationsystem.client.queries.GCUBEServiceQuery;
import org.gcube.common.core.resources.GCUBEResource;
import org.gcube.common.core.resources.GCUBERunningInstance;
import org.gcube.common.core.resources.GCUBEService;
import org.gcube.common.core.scope.GCUBEScope;
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.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.PackageItem;
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.SoftwareList;
import org.gcube.vremanagement.resourcemanager.stubs.reporting.ReportingPortType;


/**
 * @author Daniele Strollo (ISTI-CNR)
 *
 */
public class RunningInstanceManager extends AbstractResourceManager {
	// Used internally to require static functionalities (e.g. deploy).
	private static final String LOG_PREFIX = "[RI-MGR]";

	/**
	 * @deprecated discouraged use. With no ID some operations cannot be accessed.
	 */
	public RunningInstanceManager()
	throws ResourceParameterException, ResourceAccessException {
		super(AllowedResourceTypes.RunningInstance);
	}

	public RunningInstanceManager(final String id)
	throws ResourceParameterException, ResourceAccessException {
		super(id, AllowedResourceTypes.RunningInstance);
	}

	public RunningInstanceManager(final String id, final String name)
	throws ResourceParameterException, ResourceAccessException {
		super(id, name, AllowedResourceTypes.RunningInstance);
	}

	public RunningInstanceManager(final String id, final String name, final String subType)
	throws ResourceParameterException, ResourceAccessException {
		super(id, name, AllowedResourceTypes.RunningInstance, subType);
	}

	public final String deploy(
			final GCUBEScope scope, final String[] ghnsID, final String[] servicesID)
	throws ResourceParameterException, ResourceOperationException {
		Assertion<ResourceParameterException> checker = new Assertion<ResourceParameterException>();
		checker.validate(servicesID != null && servicesID.length != 0,
				new ResourceParameterException("Invalid servicesID parameter. It cannot be null or empty."));
		checker.validate(scope != null,
				new ResourceParameterException("Cannot retrieve the scope."));

		List<PackageItem> serviceProfiles = new Vector<PackageItem>();

		try {
			// Retrieves the profiles of services
			final GCUBEServiceQuery query = this.getISClient().getQuery(GCUBEServiceQuery.class);
			prepareServices: for (String serviceID : servicesID) {
				query.addAtomicConditions(new AtomicCondition("/ID", serviceID));
				List<GCUBEService> results = this.getISClient().execute(query, scope);
				GCUBEService ret = null;
				if (results != null && results.size() > 0) {
					ret = results.get(0);
				} else {
					continue prepareServices;
				}

				if (ret == null ||
						ret.getServiceClass() == null ||
						ret.getServiceName() == null ||
						ret.getVersion() == null) {
					ServerConsole.error(LOG_PREFIX, "found an invalid service profile");
					continue;
				}

				PackageItem toAdd = new PackageItem();

				toAdd.setServiceClass(ret.getServiceClass());
				toAdd.setServiceName(ret.getServiceName());
				toAdd.setServiceVersion(ret.getVersion());
				if (ret.getPackages().size() == 1) {
					toAdd.setPackageName(ret.getPackages().get(0).getName());
					toAdd.setPackageVersion(ret.getPackages().get(0).getVersion());
				} else {
					for (org.gcube.common.core.resources.service.Package p : ret.getPackages()) {
						if (p.getClass().isAssignableFrom(org.gcube.common.core.resources.service.MainPackage.class)) {
							toAdd.setPackageName(p.getName());
							toAdd.setPackageVersion(p.getVersion());
							break;
						}
					}
				}

				serviceProfiles.add(toAdd);
			}

			SoftwareList serviceList = new SoftwareList();

			// The GHNs are optional, suggested gHNs to use.
			if (ghnsID != null && ghnsID.length > 0) {
				serviceList.setSuggestedTargetGHNNames(ghnsID);
			}
	
			serviceList.setSoftware(serviceProfiles.toArray(new PackageItem[0]));

			AddResourcesParameters addResourcesParameters = new AddResourcesParameters();
			addResourcesParameters.setSoftware(serviceList);
			addResourcesParameters.setTargetScope(scope.toString());

			String reportID = this.getResourceManager(scope).addResources(addResourcesParameters);
			ServerConsole.debug(LOG_PREFIX, "Report ID = " + reportID);
			return reportID;
		} catch (Exception e) {
			ServerConsole.error(LOG_PREFIX, "Error during deployment.", e);
			throw new ResourceOperationException("Software deployment failure: " + e.getMessage());
		}
	}

	public final String undeploy(
			final GCUBEScope scope)
	throws AbstractResourceException {
		Assertion<AbstractResourceException> checker = new Assertion<AbstractResourceException>();
		checker.validate(scope != null,
				new ResourceParameterException("Cannot retrieve the scope."));
		checker.validate(this.getID() != null,
				new ResourceOperationException("Invalid ID."));

		try {
			ResourceBinderPortType rm = this.getResourceManager(scope);
			//prepare the parameters
			RemoveResourcesParameters params = new RemoveResourcesParameters();
			ResourceItem[] resourcelist = new ResourceItem[1];
			resourcelist[0] = new ResourceItem();
			resourcelist[0].setID(this.getID());
			resourcelist[0].setType(this.getType().name());
			ResourceList r = new ResourceList();
			r.setResource(resourcelist);
			params.setResources(r);
			params.setTargetScope(scope.toString());

			//sending the request
			ServerConsole.info(LOG_PREFIX, "Sending the Remove Resource request....");
			String reportID = rm.removeResources(params);
			ServerConsole.info(LOG_PREFIX, "Returned report ID: " + reportID);
			return reportID;
		} catch (Exception e) {
			ServerConsole.error(LOG_PREFIX, "Error during deployment.", e);
			throw new ResourceOperationException("Software deployment failure: " + e.getMessage());
		}
	}

	public final String checkDeployStatus(final GCUBEScope scope, final String deployID)
	throws AbstractResourceException {
		Assertion<ResourceParameterException> checker = new Assertion<ResourceParameterException>();
		checker.validate(scope != null,
				new ResourceParameterException("Invalid scope passed"));
		checker.validate(deployID != null && deployID.trim().length() > 0,
				new ResourceParameterException("Invalid reportID passed"));

		ReportingPortType vreManagerPortType = this.getReportResourceManager(scope);

		try {
			return vreManagerPortType.getReport(deployID);
		} catch (Exception e) {
			ServerConsole.error(LOG_PREFIX, e);
			throw new ResourceOperationException("Cannot retrieve the report: " + deployID + " " + e.getMessage());
		}
	}

	@Override
	protected final GCUBEResource buildGCUBEResource(final String xmlRepresentation)
			throws AbstractResourceException {
		try {
			GCUBERunningInstance impl =	GHNContext.getImplementation(GCUBERunningInstance.class);
			impl.load(new StringReader(xmlRepresentation));
			return impl;
		} catch (Exception e) {
			throw new ResourceAccessException("Cannot load the stub for resource " + this.getType(), e);
		}
	}
}
