/*
 * 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 org.apache.aries.subsystem.core.internal;

import org.apache.aries.subsystem.ContentHandler;
import org.apache.aries.subsystem.core.archive.DeployedContentHeader;
import org.apache.aries.subsystem.core.archive.DeploymentManifest;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.resource.Resource;
import org.osgi.service.coordinator.Coordination;
import org.osgi.service.subsystem.SubsystemConstants;
import org.osgi.service.subsystem.SubsystemException;

public abstract class ResourceInstaller {
	public static ResourceInstaller newInstance(Coordination coordination, Resource resource, BasicSubsystem subsystem) {
		String type = ResourceHelper.getTypeAttribute(resource);
		if (SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION.equals(type)
				|| SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE.equals(type)
				|| SubsystemConstants.SUBSYSTEM_TYPE_FEATURE.equals(type)) {
			return new SubsystemResourceInstaller(coordination, resource, subsystem);
		} else if (IdentityNamespace.TYPE_BUNDLE.equals(type) || IdentityNamespace.TYPE_FRAGMENT.equals(type)) {
			return new BundleResourceInstaller(coordination, resource, subsystem);
		} else if (Constants.ResourceTypeSynthesized.equals(type)) {
			return new ResourceInstaller(coordination, resource, subsystem) {
				@Override
				public Resource install() throws Exception {
					// do nothing;
					return resource;
				}
			};
		} else {
		    ServiceReference<ContentHandler> handlerRef = CustomResources.getCustomContentHandler(subsystem, type);
		    if (handlerRef != null)
		        return new CustomResourceInstaller(coordination, resource, type, subsystem, handlerRef);

		}
		throw new SubsystemException("No installer exists for resource type: " + type);
	}

    protected final Coordination coordination;
	protected final BasicSubsystem provisionTo;
	/* resource to install */
	protected final Resource resource;
	/* parent subsystem being installed into */
	protected final BasicSubsystem subsystem;

	public ResourceInstaller(Coordination coordination, Resource resource, BasicSubsystem subsystem) {
		if (coordination == null || resource == null || subsystem == null) {
			// We're assuming these are not null post construction, so enforce it here.
			throw new NullPointerException();
		}
		this.coordination = coordination;
		this.resource = resource;
		this.subsystem = subsystem;
		if (isDependency()) {
			if (Utils.isInstallableResource(resource))
				provisionTo = Utils.findFirstSubsystemAcceptingDependenciesStartingFrom(subsystem);
			else
				provisionTo = null;
		}
		else
			provisionTo = subsystem;
	}

	public abstract Resource install() throws Exception;

	protected void addConstituent(final Resource resource) {
		// Don't let a resource become a constituent of itself.
		if (provisionTo == null || resource.equals(provisionTo))
			return;
		Activator.getInstance().getSubsystems().addConstituent(provisionTo, resource, isReferencedProvisionTo());
	}

	protected void addReference(final Resource resource) {
		// Don't let a resource reference itself.
		if (resource.equals(subsystem))
			return;
		// The following check protects against resources posing as content
		// during a restart since the Deployed-Content header is currently used
		// to track all constituents for persistence purposes, which includes
		// resources that were provisioned to the subsystem as dependencies of
		// other resources.
		if (isReferencedSubsystem()) {
			Activator.getInstance().getSubsystems().addReference(subsystem, resource);
		}
	}

	protected String getLocation() {
		return provisionTo.getLocation() + "!/" + ResourceHelper.getLocation(resource);
	}

	protected boolean isContent() {
		return Utils.isContent(subsystem, resource);
	}

	protected boolean isDependency() {
		return Utils.isDependency(subsystem, resource);
	}

	protected boolean isReferencedProvisionTo() {
		DeploymentManifest manifest = subsystem.getDeploymentManifest();
		if (manifest != null) {
			DeployedContentHeader header = manifest.getDeployedContentHeader();
			if (header != null && header.contains(resource))
				return subsystem.isReferenced(resource);
		}
		if (subsystem.equals(provisionTo))
			return isReferencedSubsystem();
		return false;
	}

	protected boolean isReferencedSubsystem() {
		DeploymentManifest manifest = subsystem.getDeploymentManifest();
		if (manifest != null) {
			DeployedContentHeader header = manifest.getDeployedContentHeader();
			if (header != null && header.contains(resource))
				return subsystem.isReferenced(resource);
		}
		return true;
	}
}
