/*
 * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors.
 *
 * 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.switchyard.as7.extension.deployment;

import java.util.HashSet;
import java.util.Set;

import org.jboss.as.clustering.infinispan.subsystem.CacheService;
import org.jboss.as.connector.util.ConnectorServices;
import org.jboss.as.ee.component.EEModuleDescription;
import org.jboss.as.ee.weld.WeldDeploymentMarker;
import org.jboss.as.naming.context.NamespaceContextSelector;
import org.jboss.as.naming.deployment.JndiNamingDependencyProcessor;
import org.jboss.as.server.deployment.Attachments;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.web.host.WebHost;
import org.jboss.as.weld.WeldStartService;
import org.jboss.as.weld.services.BeanManagerService;
import org.jboss.logging.Logger;
import org.jboss.metadata.ear.spec.EarMetaData;
import org.jboss.modules.Module;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceBuilder.DependencyType;
import org.jboss.msc.service.ServiceController.Mode;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceTarget;
import org.jboss.msc.value.ImmediateValue;
import org.switchyard.as7.extension.SwitchYardDeploymentMarker;
import org.switchyard.as7.extension.SwitchYardModuleAdd;
import org.switchyard.as7.extension.services.SwitchYardComponentService;
import org.switchyard.as7.extension.services.SwitchYardService;
import org.switchyard.as7.extension.services.SwitchYardServiceDomainManagerService;
import org.switchyard.component.jca.config.model.InboundConnectionModel;
import org.switchyard.component.jca.config.model.JCABindingModel;
import org.switchyard.component.jca.config.model.OutboundConnectionModel;
import org.switchyard.component.jca.config.model.ResourceAdapterModel;
import org.switchyard.config.model.composite.BindingModel;
import org.switchyard.config.model.composite.CompositeModel;
import org.switchyard.config.model.composite.CompositeReferenceModel;
import org.switchyard.config.model.composite.CompositeServiceModel;
import org.switchyard.deploy.Component;
import org.switchyard.deploy.ServiceDomainManager;

/**
 * Deployment processor that installs the SwitchYard service and all other dependent services.
 * 
 * @author Magesh Kumar B <mageshbk@jboss.com> (C) 2011 Red Hat Inc.
 */
public class SwitchYardDeploymentProcessor implements DeploymentUnitProcessor {

    private static final Logger LOG = Logger.getLogger("org.switchyard");

    /**
     * Construct SwitchYard deployment processor with a list of component modules.
     */
    public SwitchYardDeploymentProcessor() {
    }

    @Override
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        if (!SwitchYardDeploymentMarker.isSwitchYardDeployment(deploymentUnit)) {
            return;
        }
        final DeploymentUnit parent = deploymentUnit.getParent();
        Boolean initializeInOrder = false;
        if (parent != null) {
            final EarMetaData earConfig = deploymentUnit.getParent().getAttachment(org.jboss.as.ee.structure.Attachments.EAR_METADATA);
            if (earConfig != null) {
                initializeInOrder = earConfig.getInitializeInOrder();
            }
        }
        doDeploy(phaseContext, deploymentUnit, initializeInOrder);
    }

    private void doDeploy(DeploymentPhaseContext phaseContext, final DeploymentUnit deploymentUnit, final Boolean initializeInOrder) {
        LOG.info("Deploying SwitchYard application '" + deploymentUnit.getName() + "'");

        ServiceDomainManager domainManager =
                (ServiceDomainManager) phaseContext.getServiceRegistry().getRequiredService(SwitchYardServiceDomainManagerService.SERVICE_NAME).getService().getValue();

        final ServiceTarget serviceTarget = phaseContext.getServiceTarget();
        SwitchYardMetaData metaData = deploymentUnit.getAttachment(SwitchYardMetaData.ATTACHMENT_KEY);
        SwitchYardDeployment deployment = new SwitchYardDeployment(deploymentUnit, metaData.getSwitchYardModel(), domainManager);
        SwitchYardService container = new SwitchYardService(deployment);
        final ServiceName switchyardServiceName = deploymentUnit.getServiceName().append(SwitchYardService.SERVICE_NAME);
        final ServiceBuilder<SwitchYardDeployment> switchyardServiceBuilder = serviceTarget.addService(switchyardServiceName, container);
        for (String componentName : SwitchYardModuleAdd.getComponentNames()) {
            switchyardServiceBuilder.addDependency(SwitchYardComponentService.SERVICE_NAME.append(componentName), Component.class, container.getComponent());
        }
        // ensure naming context is fully initialized before we start
        switchyardServiceBuilder.addDependency(JndiNamingDependencyProcessor.serviceName(deploymentUnit));

        final EEModuleDescription moduleDescription = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.EE_MODULE_DESCRIPTION);
        if (moduleDescription != null) {
            container.getNamespaceSelector().setValue(new ImmediateValue<NamespaceContextSelector>(moduleDescription.getNamespaceContextSelector()));
        }

        // Only add a dependency on the Weld BeanManager if the deployment has beans (i.e. Weld Metadata)...
        if (WeldDeploymentMarker.isPartOfWeldDeployment(deploymentUnit)) {
            final ServiceName beanManagerServiceName = deploymentUnit.getServiceName().append(BeanManagerService.NAME);
            switchyardServiceBuilder.addDependency(beanManagerServiceName);
            if (deploymentUnit.getParent() == null) {
                final ServiceName weldStartServiceName = deploymentUnit.getServiceName().append(WeldStartService.SERVICE_NAME);
                switchyardServiceBuilder.addDependency(weldStartServiceName);
            } else {
                final ServiceName weldStartServiceName = deploymentUnit.getParent().getServiceName().append(WeldStartService.SERVICE_NAME);
                switchyardServiceBuilder.addDependency(weldStartServiceName);
            }
        }

        // Add WebHostService dependency
        switchyardServiceBuilder.addDependency(WebHost.SERVICE_NAME.append("default-host"));
        
        // Collect all the resource adapters referenced from SwitchYard configuration
        Set<String> resourceAdapters = new HashSet<String>();
        ClassLoader origCl = Thread.currentThread().getContextClassLoader();
        try {
            final Module module = deploymentUnit.getAttachment(Attachments.MODULE);
            Thread.currentThread().setContextClassLoader(module.getClassLoader());
            CompositeModel composite = metaData.getSwitchYardModel().getComposite();
            if (composite != null) {
                for (CompositeServiceModel service : composite.getServices()) {
                    for (BindingModel binding : service.getBindings()) {
                        if (binding instanceof JCABindingModel) {
                            JCABindingModel jcabinding = JCABindingModel.class.cast(binding);
                            InboundConnectionModel ic = jcabinding.getInboundConnection();
                            if (ic != null) {
                                ResourceAdapterModel ra = ic.getResourceAdapter();
                                if (ra != null && ra.getName() != null && !ra.getName().isEmpty()) {
                                    resourceAdapters.add(ra.getName());
                                }
                            }
                        }
                    }
                }
                for (CompositeReferenceModel reference : composite.getReferences()) {
                    for (BindingModel binding : reference.getBindings()) {
                        if (binding instanceof JCABindingModel) {
                            JCABindingModel jcabinding = JCABindingModel.class.cast(binding);
                            OutboundConnectionModel oc = jcabinding.getOutboundConnection();
                            if (oc != null) {
                                ResourceAdapterModel ra = oc.getResourceAdapter();
                                if (ra != null && ra.getName() != null && !ra.getName().isEmpty()) {
                                    resourceAdapters.add(ra.getName());
                                }
                            }
                        }
                    }
                }
            } else {
                LOG.info("A composite element is missing from the switchyard.xml");
            }
        } finally {
                Thread.currentThread().setContextClassLoader(origCl);
        }
        for (String raName : resourceAdapters) {
            switchyardServiceBuilder.addDependency(ConnectorServices.RESOURCE_ADAPTER_SERVICE_PREFIX
                                                                    .append(stripDotRarSuffix(raName)));
        }

        // Add dependency in the order defined in ear config
        if (initializeInOrder) {
            final DeploymentUnit parent = deploymentUnit.getParent();
            ServiceName previousServiceName = parent.getAttachment(SwitchYardMetaData.SERVICENAME_ATTACHMENT_KEY);
            if (previousServiceName != null) {
                switchyardServiceBuilder.addDependency(previousServiceName);
            }
            parent.putAttachment(SwitchYardMetaData.SERVICENAME_ATTACHMENT_KEY, switchyardServiceName);
        }

        switchyardServiceBuilder.addDependency(DependencyType.OPTIONAL, CacheService.getServiceName("cluster", null));

        switchyardServiceBuilder.setInitialMode(Mode.ACTIVE);
        switchyardServiceBuilder.install();
    }

    private String stripDotRarSuffix(final String raName) {
        if (raName == null) {
            return null;
        }
        if (raName.endsWith(".rar")) {
            return raName.substring(0, raName.indexOf(".rar"));
        }
        return raName;
    }

    @Override
    public void undeploy(DeploymentUnit deploymentUnit) {
    }

}
