/*
 * #%L
 * Fuse EAP :: Config
 * %%
 * Copyright (C) 2015 RedHat
 * %%
 * 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.
 * #L%
 */
package org.jboss.fuse.eap.config;

import static org.wildfly.extras.config.NamespaceConstants.NS_DOMAIN;
import static org.wildfly.extras.config.NamespaceConstants.NS_INFINISPAN;
import static org.wildfly.extras.config.NamespaceConstants.NS_LOGGING;
import static org.wildfly.extras.config.NamespaceConstants.NS_SECURITY;

import java.net.URL;
import java.util.Arrays;
import java.util.List;

import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.Text;
import org.wildfly.extras.config.ConfigContext;
import org.wildfly.extras.config.ConfigPlugin;
import org.wildfly.extras.config.ConfigSupport;
import org.wildfly.extras.config.LayerConfig;
import org.wildfly.extras.config.LayerConfig.Type;
import org.wildfly.extras.config.NamespaceRegistry;

public class SwitchyardConfigPlugin implements ConfigPlugin {

    public static final String NS_SWITCHYARD = "urn:jboss:domain:switchyard";

    private NamespaceRegistry registry;

    public SwitchyardConfigPlugin() {
        registerNamespaceVersions(new NamespaceRegistry());
    }

    public SwitchyardConfigPlugin(NamespaceRegistry registry) {
        registerNamespaceVersions(registry);
    }

    @Override
    public String getConfigName() {
        return "switchyard";
    }

    @Override
    public List<LayerConfig> getLayerConfigs() {
        return Arrays.asList(new LayerConfig("fuse", Type.INSTALLING, -10), new LayerConfig("bpms", Type.OPTIONAL, -9), new LayerConfig("soa", Type.INSTALLING, -8));
    }
    
    @Override
    public void applyStandaloneConfigChange(ConfigContext context, boolean enable) {
        String fileName = context.getConfiguration().getFileName().toString();
        updateExtension(context, enable);
        updateLogging(context, enable);
        updateSecurityDomain(context, enable);
        updateSubsystem(context, enable);
        if (fileName.contains("-ha")) {
            updateInfinispan(true, context);
        }
    }

    @Override
    public void applyDomainConfigChange(ConfigContext context, boolean enable) {
        applyStandaloneConfigChange(context, enable);
        updateInfinispan(enable, context);
    }

    private void updateExtension(ConfigContext context, boolean enable) {
        Namespace[] domainNamespaces = registry.getNamespaces(NS_DOMAIN);
        Element extensions = ConfigSupport.findChildElement(context.getDocument().getRootElement(), "extensions", domainNamespaces);
        ConfigSupport.assertExists(extensions, "Did not find the <extensions> element");
        Element element = ConfigSupport.findElementWithAttributeValue(extensions, "extension", "module", "org.switchyard", domainNamespaces);

        Namespace domainNamespace = context.getDocument().getRootElement().getNamespace();
        if (enable && element == null) {
            extensions.addContent(new Text("    "));
            extensions.addContent(new Element("extension", domainNamespace).setAttribute("module", "org.switchyard"));
            extensions.addContent(new Text("\n    "));
        }
        if (!enable && element != null) {
            element.getParentElement().removeContent(element);
        }
    }

    private void updateLogging(ConfigContext context, boolean enable) {
        Namespace[] domainNamespaces = registry.getNamespaces(NS_DOMAIN);
        Namespace[] loggingNamespaces = registry.getNamespaces(NS_LOGGING);

        List<Element> profiles = ConfigSupport.findProfileElements(context.getDocument(), domainNamespaces);
        for (Element profile : profiles) {
            Element logging = ConfigSupport.findChildElement(profile, "subsystem", loggingNamespaces);
            ConfigSupport.assertExists(logging, "Did not find the logging subsystem");

            Element element = ConfigSupport.findElementWithAttributeValue(logging, "logger", "category", "org.switchyard", loggingNamespaces);
            if (enable && element == null) {
                URL resource = SwitchyardConfigPlugin.class.getResource("/logger-switchyard.xml");
                logging.addContent(new Text("    "));
                logging.addContent(ConfigSupport.loadElementFrom(resource));
                logging.addContent(new Text("\n    "));
            }
            if (!enable && element != null) {
                element.getParentElement().removeContent(element);
            }

            element = ConfigSupport.findElementWithAttributeValue(logging, "logger", "category", "org.apache.deltaspike.core.api.provider.BeanManagerProvider", loggingNamespaces);
            if (enable && element == null) {
                URL resource = SwitchyardConfigPlugin.class.getResource("/logger-deltaspike.xml");
                logging.addContent(new Text("    "));
                logging.addContent(ConfigSupport.loadElementFrom(resource));
                logging.addContent(new Text("\n    "));
            }
            if (!enable && element != null) {
                element.getParentElement().removeContent(element);
            }
        }
    }

    private void updateSubsystem(ConfigContext context, boolean enable) {
        Namespace[] domainNamespaces = registry.getNamespaces(NS_DOMAIN);
        Namespace[] switchYardNamespaces = registry.getNamespaces(NS_SWITCHYARD);

        List<Element> profiles = ConfigSupport.findProfileElements(context.getDocument(), domainNamespaces);
        for (Element profile : profiles) {
            Element element = ConfigSupport.findChildElement(profile, "subsystem", switchYardNamespaces);
            if (enable && element == null) {
                URL resource = SwitchyardConfigPlugin.class.getResource("/switchyard-subsystem.xml");
                profile.addContent(new Text("    "));
                profile.addContent(ConfigSupport.loadElementFrom(resource));
                profile.addContent(new Text("\n    "));
            }
            if (!enable && element != null) {
                element.getParentElement().removeContent(element);
            }
        }
    }

    private void updateSecurityDomain(ConfigContext context, boolean enable) {
        Namespace[] domainNamespaces = registry.getNamespaces(NS_DOMAIN);
        Namespace[] securityNamespaces = registry.getNamespaces(NS_SECURITY);

        List<Element> profiles = ConfigSupport.findProfileElements(context.getDocument(), domainNamespaces);
        for (Element profile : profiles) {
            Element security = ConfigSupport.findChildElement(profile, "subsystem", securityNamespaces);
            ConfigSupport.assertExists(security, "Did not find the security subsystem");
            Element domains = ConfigSupport.findChildElement(security, "security-domains", securityNamespaces);
            ConfigSupport.assertExists(domains, "Did not find the <security-domains> element");
            Element domain = ConfigSupport.findElementWithAttributeValue(domains, "security-domain", "name", "bpel-console", securityNamespaces);
            if (enable && domain == null) {
                URL resource = SwitchyardConfigPlugin.class.getResource("/bpel-console-security-domain.xml");
                domains.addContent(new Text("    "));
                domains.addContent(ConfigSupport.loadElementFrom(resource));
                domains.addContent(new Text("\n            "));
            }
            if (!enable && domain != null) {
                domain.getParentElement().removeContent(domain);
            }
        }
    }

    private void updateInfinispan(boolean enable, ConfigContext context) {
        Namespace[] domainNamespaces = registry.getNamespaces(NS_DOMAIN);
        Namespace[] infinispanNamespaces = registry.getNamespaces(NS_INFINISPAN);

        List<Element> profiles = ConfigSupport.findProfileElements(context.getDocument(), domainNamespaces);
        for (Element profile : profiles) {
            Element infinispan = ConfigSupport.findChildElement(profile, "subsystem", infinispanNamespaces);
            ConfigSupport.assertExists(infinispan, "Did not find the cache subsystem");
            Element element = ConfigSupport.findElementWithAttributeValue(infinispan, "cache-container", "name", "switchyard", infinispanNamespaces);
            if (enable && element == null) {
                URL resource = SwitchyardConfigPlugin.class.getResource("/switchyard-cache-container.xml");
                infinispan.addContent(new Text("    "));
                infinispan.addContent(ConfigSupport.loadElementFrom(resource));
                infinispan.addContent(new Text("\n    "));
            }
            if (!enable && element != null) {
                element.getParentElement().removeContent(element);
            }
        }
    }

    private void registerNamespaceVersions(NamespaceRegistry registry) {
        this.registry = registry;
        registry.registerNamespace(NS_DOMAIN, "1.8");
        registry.registerNamespace(NS_DOMAIN, "1.7");
        registry.registerNamespace(NS_INFINISPAN, "1.5");
        registry.registerNamespace(NS_LOGGING, "1.5");
        registry.registerNamespace(NS_SECURITY, "1.2");
        registry.registerNamespace(NS_SWITCHYARD, "1.0");
    }
}
