/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2017, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.wildfly.extension.undertow.deployment;

import io.undertow.server.handlers.builder.PredicatedHandler;
import io.undertow.server.handlers.builder.PredicatedHandlersParser;
import org.jboss.as.server.deployment.AttachmentKey;
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.server.deployment.module.ResourceRoot;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
import org.jboss.metadata.web.jboss.HttpHandlerMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.vfs.VirtualFile;
import org.wildfly.extension.undertow.logging.UndertowLogger;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * DUP that handles undertow-handlers.conf, and handlers definded in jboss-web.xml
 *
 * @author Stuart Douglas
 */
public class UndertowHandlersDeploymentProcessor implements DeploymentUnitProcessor {

    private static final String WEB_INF = "WEB-INF/undertow-handlers.conf";
    private static final String META_INF = "META-INF/undertow-handlers.conf";

    public static final AttachmentKey<List<PredicatedHandler>> PREDICATED_HANDLERS = AttachmentKey.create(List.class);

    @Override
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        final Module module = deploymentUnit.getAttachment(Attachments.MODULE);
        if (module == null) {
            return;
        }
        handleInfoFile(deploymentUnit, module);
        handleJbossWebXml(deploymentUnit, module);
    }

    private void handleJbossWebXml(DeploymentUnit deploymentUnit, Module module) throws DeploymentUnitProcessingException {
        WarMetaData warMetadata = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
        if (warMetadata == null) {
            return;
        }
        JBossWebMetaData merged = warMetadata.getMergedJBossWebMetaData();
        if (merged == null) {
            return;
        }
        List<HttpHandlerMetaData> handlers = merged.getHandlers();
        if (handlers == null) {
            return;
        }
        for (HttpHandlerMetaData hander : handlers) {
            try {
                ClassLoader cl = module.getClassLoader();
                if (hander.getModule() != null) {
                    Module handlerModule = deploymentUnit.getAttachment(Attachments.SERVICE_MODULE_LOADER).loadModule(ModuleIdentifier.fromString(hander.getModule()));
                    cl = handlerModule.getClassLoader();

                }
                Class<?> handlerClass = cl.loadClass(hander.getHandlerClass());
                Map<String, String> params = new HashMap<>();
                if(hander.getParams() != null) {
                    for(ParamValueMetaData param : hander.getParams()) {
                        params.put(param.getParamName(), param.getParamValue());
                    }
                }
                deploymentUnit.addToAttachmentList(UndertowAttachments.UNDERTOW_OUTER_HANDLER_CHAIN_WRAPPERS, new ConfiguredHandlerWrapper(handlerClass, params));
            } catch (Exception e) {
                throw UndertowLogger.ROOT_LOGGER.failedToConfigureHandlerClass(hander.getHandlerClass(), e);
            }
        }

    }

    private void handleInfoFile(DeploymentUnit deploymentUnit, Module module) {
        final List<PredicatedHandler> handlerWrappers = new ArrayList<>();
        ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
        try {
            ResourceRoot root = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_ROOT);
            VirtualFile config = root.getRoot().getChild(WEB_INF);
            try {
                if (config.exists()) {
                    handlerWrappers.addAll(PredicatedHandlersParser.parse(config.openStream(), module.getClassLoader()));
                }
                Enumeration<URL> paths = module.getClassLoader().getResources(META_INF);
                while (paths.hasMoreElements()) {
                    URL path = paths.nextElement();
                    handlerWrappers.addAll(PredicatedHandlersParser.parse(path.openStream(), module.getClassLoader()));
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            if (!handlerWrappers.isEmpty()) {
                deploymentUnit.putAttachment(PREDICATED_HANDLERS, handlerWrappers);
            }

        } finally {
            Thread.currentThread().setContextClassLoader(oldCl);
        }
    }
}
