/****************************************************************************
 *  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: GenericResourceManager.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 org.gcube.common.core.contexts.GHNContext;
import org.gcube.common.core.informationsystem.publisher.ISPublisher;
import org.gcube.common.core.resources.GCUBEGenericResource;
import org.gcube.common.core.resources.GCUBEResource;
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.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.w3c.dom.Document;


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

	static {
		if (GenericResourceManager.singleton == null) {
			try {
				GenericResourceManager.singleton = new GenericResourceManager("dummyservice", "dummyservice");
			} catch (Exception e) {
				ServerConsole.error(LOG_PREFIX, e);
			}
		}
	}

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

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

	/**
	 * @param id
	 * @param name
	 * @param type
	 * @throws ResourceParameterException
	 * @throws ResourceAccessException
	 */
	public GenericResourceManager(final String id, final String name)
	throws ResourceParameterException,
	ResourceAccessException {
		super(id, name, AllowedResourceTypes.GenericResource);
	}

	/**
	 * @param id
	 * @param name
	 * @param type
	 * @param subtype
	 * @throws ResourceParameterException
	 * @throws ResourceAccessException
	 */
	public GenericResourceManager(final String id, final String name, final String subtype)
	throws ResourceParameterException, ResourceAccessException {
		super(id, name, AllowedResourceTypes.GenericResource, subtype);
	}

	/**
	 * Updates the resource.
	 * @param name (Mandatory) the name to assign to the resource
	 * @param description (optional) if null it will not be changed
	 * @param body (optional) if null it will not be changed
	 * @param subType (optional) if null it will not be changed
	 * @param scope (optional) if null it will not be changed
	 * @throws AbstractResourceException
	 */
	public final void update(
			final String name,
			final String description,
			final String body,
			final String subType,
			final GCUBEScope scope)
	throws AbstractResourceException {
		Assertion<AbstractResourceException> checker = new Assertion<AbstractResourceException>();
		checker.validate(name != null && name.trim().length() != 0, new ResourceParameterException("Invalid field name. Null or empty value not allowed"));

		ServerConsole.trace(LOG_PREFIX, "[RES-UPDATE] updating resource " + this.getType() + " " + this.getID());

		GCUBEGenericResource res = (GCUBEGenericResource) this.getGCUBEResource(scope);
		res.setName(name.trim());
		if (description != null) {
			res.setDescription(description.trim());
		}
		if (body != null) {
			res.setBody(body.trim());
		}
		if (subType != null) {
			res.setSecondaryType(subType.trim());
		}
		try {
			this.getISPublisher().updateGCUBEResource(res, scope, this.getSecurityManager());

			/* FIXME old release
			 * List<GCUBEScope> boundedScopes = this.validateScopes(res.getScopes().values().toArray(new GCUBEScope[]{}));


			for (GCUBEScope _scope : boundedScopes) {
				ServerConsole.trace(LOG_PREFIX, "[RES-UPDATE] ISPublisher updating resource " + this.getType() + " " + this.getID() + " in scope: [" + _scope + "]");
				this.getISPublisher().updateGCUBEResource(res, _scope, this.getSecurityManager());
			}
			 */
		} catch (Exception e) {
			throw new ResourceOperationException(e.getMessage());
		}
	}

	/**
	 * Creates a Generic Resource and returns the ID given by the
	 * resource manager at creation phase.
	 * @return the id assigned to the newly created resource
	 */
	public static final synchronized String create(
			final String ID,
			final GCUBEScope scope,
			final String name,
			final String description,
			final String body,
			final String subType)
	throws Exception {
		Assertion<AbstractResourceException> checker = new Assertion<AbstractResourceException>();
		checker.validate(name != null && name.trim().length() != 0, new ResourceParameterException("Invalid field name. Null or empty value not allowed"));
		checker.validate(subType != null && subType.trim().length() != 0, new ResourceParameterException("Invalid field subType. Null or empty value not allowed"));

		GCUBEGenericResource resource = GHNContext.getImplementation(GCUBEGenericResource.class);
		if (ID != null) {
			resource.setID(ID);
		}
		resource.setName(name.trim());
		if (description != null) {
			resource.setDescription(description.trim());
		}
		if (body != null) {
			resource.setBody(body.trim());
		}
		resource.setSecondaryType(subType.trim());
		resource.addScope(scope);

		GenericResourceManager gm = new GenericResourceManager();
		ISPublisher publisher = gm.getISPublisher();
		String retval = publisher.registerGCUBEResource(resource, scope, gm.getSecurityManager());

		if (retval == null || retval.length() == 0) {
			throw new Exception("The GenericResource has not been created");
		}

		Document doc = ScopeManager.getDocumentGivenXML(retval);
		String id = doc.getElementsByTagName("ID").item(0).getFirstChild().getNodeValue();

		ServerConsole.info(LOG_PREFIX, "Resource Created with ID: " + id);
		return id;
	}

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

}
