/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.camel.quarkus.support.language.deployment;

import java.util.ArrayList;
import java.util.List;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.quarkus.core.CamelConfig;
import org.apache.camel.quarkus.core.deployment.spi.CamelRoutesBuilderClassBuildItem;
import org.apache.camel.quarkus.support.language.deployment.dm.DryModeLanguage;
import org.apache.camel.quarkus.support.language.deployment.dm.DryModeMain;
import org.apache.camel.quarkus.support.language.deployment.dm.ExpressionHolder;
import org.apache.camel.quarkus.support.language.deployment.dm.ScriptHolder;
import org.jboss.logging.Logger;

class LanguageSupportProcessor {

    private static final Logger LOG = Logger.getLogger(LanguageSupportProcessor.class);

    @BuildStep
    ExpressionExtractionResultBuildItem extractExpressions(CamelConfig config,
            List<CamelRoutesBuilderClassBuildItem> routesBuilderClasses,
            BuildProducer<ExpressionBuildItem> expressions,
            BuildProducer<ScriptBuildItem> scripts) throws Exception {
        CamelConfig.ExpressionConfig expression = config.expression();
        if (expression.extractionEnabled()) {
            final ClassLoader loader = Thread.currentThread().getContextClassLoader();
            if (!(loader instanceof QuarkusClassLoader)) {
                throw new IllegalStateException(
                        QuarkusClassLoader.class.getSimpleName() + " expected as the context class loader");
            }
            final List<Class<?>> routeBuilderClasses = new ArrayList<>(routesBuilderClasses.size());
            for (CamelRoutesBuilderClassBuildItem routesBuilderClass : routesBuilderClasses) {
                final String className = routesBuilderClass.getDotName().toString();
                final Class<?> cl = loader.loadClass(className);

                if (RouteBuilder.class.isAssignableFrom(cl)) {
                    routeBuilderClasses.add(cl);
                } else {
                    LOG.warnf("Language expressions occurring in %s won't be compiled at build time", cl);
                }
            }
            try {
                DryModeMain main = new DryModeMain("Expression Extractor", routeBuilderClasses.toArray(new Class<?>[0]));
                main.start();
                main.run();
                for (DryModeLanguage language : main.getLanguages()) {
                    final String name = language.getName();
                    for (ExpressionHolder holder : language.getPredicates()) {
                        expressions.produce(new ExpressionBuildItem(name, holder.getContent(), holder.getLoadedContent(),
                                holder.getProperties(), true));
                    }
                    for (ExpressionHolder holder : language.getExpressions()) {
                        expressions.produce(new ExpressionBuildItem(name, holder.getContent(), holder.getLoadedContent(),
                                holder.getProperties(), false));
                    }
                    for (ScriptHolder script : language.getScripts()) {
                        scripts.produce(
                                new ScriptBuildItem(name, script.getContent(), script.getLoadedContent(),
                                        script.getBindings()));
                    }
                }
                return new ExpressionExtractionResultBuildItem(true);
            } catch (Exception e) {
                switch (expression.onBuildTimeAnalysisFailure()) {
                case fail:
                    throw new RuntimeException(
                            "Could not extract language expressions."
                                    + "You may want to set quarkus.camel.expression.on-build-time-analysis-failure to warn or ignore if you do not use languages in your routes",
                            e);
                case warn:
                    LOG.warn("Could not extract language expressions.", e);
                    break;
                case ignore:
                    LOG.debug("Could not extract language expressions", e);
                    break;
                default:
                    throw new IllegalStateException("Unexpected " + CamelConfig.FailureRemedy.class.getSimpleName() + ": "
                            + expression.onBuildTimeAnalysisFailure());
                }
            }
        }
        return new ExpressionExtractionResultBuildItem(false);
    }
}
