/*******************************************************************************
 * (c) Copyright 2016 Hewlett-Packard Development Company, L.P.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License v2.0 which accompany this distribution.
 *
 * The Apache License is available at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *******************************************************************************/
package io.cloudslang.lang.tools.build.verifier;

import io.cloudslang.lang.commons.services.api.SlangCompilationService;
import io.cloudslang.lang.compiler.MetadataExtractor;
import io.cloudslang.lang.compiler.SlangCompiler;
import io.cloudslang.lang.compiler.SlangSource;
import io.cloudslang.lang.compiler.modeller.model.Executable;
import io.cloudslang.lang.compiler.modeller.model.Metadata;
import io.cloudslang.lang.compiler.modeller.result.ExecutableModellingResult;
import io.cloudslang.lang.compiler.modeller.result.MetadataModellingResult;
import io.cloudslang.lang.compiler.scorecompiler.ScoreCompiler;
import io.cloudslang.lang.entities.CompilationArtifact;
import io.cloudslang.lang.logging.LoggingService;
import io.cloudslang.lang.tools.build.validation.MetadataMissingException;
import io.cloudslang.lang.tools.build.validation.StaticValidator;
import java.io.File;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.apache.log4j.Level;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Created by stoneo on 3/15/2015.
 **/
@Component
public class SlangContentVerifier {

    @Autowired
    private SlangCompiler slangCompiler;

    @Autowired
    private MetadataExtractor metadataExtractor;

    @Autowired
    private StaticValidator staticValidator;

    @Autowired
    private ScoreCompiler scoreCompiler;

    @Autowired
    private LoggingService loggingService;

    @Autowired
    private SlangCompilationService slangCompilationService;

    public PreCompileResult createModelsAndValidate(String directoryPath, boolean shouldValidateDescription,
                                                    boolean shouldValidateCheckstyle) {
        Validate.notEmpty(directoryPath, "You must specify a path");
        Validate.isTrue(new File(directoryPath).isDirectory(), "Directory path argument \'" +
                directoryPath + "\' does not lead to a directory");
        Map<String, Executable> slangModels = new HashMap<>();
        Collection<File> slangFiles = slangCompilationService.listSlangFiles(new File(directoryPath), true);
        loggingService.logEvent(Level.INFO, "Start compiling all slang files under: " + directoryPath);
        loggingService.logEvent(Level.INFO, slangFiles.size() + " .sl files were found");
        loggingService.logEvent(Level.INFO, "");
        Queue<RuntimeException> exceptions = new ArrayDeque<>();
        String errorMessagePrefixMetadata = "";
        String errorMessagePrefixCompilation = "";
        for (File slangFile: slangFiles) {
            Executable sourceModel = null;
            try {
                errorMessagePrefixMetadata = "Failed to extract metadata for file: \'" +
                    slangFile.getAbsoluteFile() + "\'.\n";
                errorMessagePrefixCompilation = "Failed to compile file: \'" +
                    slangFile.getAbsoluteFile() + "\'.\n";

                Validate.isTrue(slangFile.isFile(), "file path \'" + slangFile.getAbsolutePath() +
                        "\' must lead to a file");
                SlangSource slangSource = SlangSource.fromFile(slangFile);

                ExecutableModellingResult preCompileResult = slangCompiler.preCompileSource(slangSource);
                sourceModel = preCompileResult.getExecutable();
                exceptions.addAll(prependPrefix(preCompileResult.getErrors(), errorMessagePrefixCompilation));

                MetadataModellingResult metadataResult = metadataExtractor
                        .extractMetadataModellingResult(slangSource, shouldValidateCheckstyle);
                Metadata sourceMetadata = metadataResult.getMetadata();
                exceptions.addAll(prependPrefix(metadataResult.getErrors(), errorMessagePrefixMetadata));

                if (sourceModel != null) {
                    int size = exceptions.size();
                    staticValidator.validateSlangFile(slangFile, sourceModel,
                                    sourceMetadata, shouldValidateDescription, exceptions);
                    if (size == exceptions.size()) {
                        slangModels.put(getUniqueName(sourceModel), sourceModel);
                    }
                }
            } catch (Exception e) {
                String errorMessage = errorMessagePrefixMetadata + e.getMessage();
                loggingService.logEvent(Level.ERROR, errorMessage);
                exceptions.add(new RuntimeException(errorMessage, e));
                if (e instanceof MetadataMissingException && sourceModel != null) {
                    slangModels.put(getUniqueName(sourceModel), sourceModel);
                }
            }
        }
        if (slangFiles.size() != slangModels.size()) {
            exceptions.add(new RuntimeException("Some Slang files were not pre-compiled.\nFound: " + slangFiles.size() +
                    " executable files in path: \'" + directoryPath +
                    "\' But managed to create slang models for only: " + slangModels.size()));
        }
        PreCompileResult preCompileResult = new PreCompileResult();
        preCompileResult.addExceptions(exceptions);
        preCompileResult.addResults(slangModels);
        return preCompileResult;
    }

    private Collection<RuntimeException> prependPrefix(Collection<RuntimeException> errors, String prefix) {
        List<RuntimeException> result = new ArrayList<>();
        for (RuntimeException ex : errors) {
            result.add(wrapWithPrefix(ex, prefix));
        }
        return result;
    }

    private RuntimeException wrapWithPrefix(RuntimeException rex, String prefix) {
        return new RuntimeException(prefix + rex.getMessage(), rex);
    }

    public CompileResult compileSlangModels(Map<String, Executable> slangModels) {
        CompileResult compileResult = new CompileResult();
        Map<String, CompilationArtifact> compiledArtifacts = new HashMap<>();
        for (Map.Entry<String, Executable> slangModelEntry : slangModels.entrySet()) {
            Executable slangModel = slangModelEntry.getValue();
            try {
                CompilationArtifact compiledSource = compiledArtifacts.get(getUniqueName(slangModel));
                if (compiledSource == null) {
                    Set<Executable> dependenciesModels = getModelDependenciesRecursively(slangModels, slangModel);
                    compiledSource = scoreCompiler.compile(slangModel, dependenciesModels);
                    if (compiledSource != null) {
                        loggingService.logEvent(Level.INFO, "Compiled: \'" + slangModel.getNamespace() + "." +
                                slangModel.getName() + "\' successfully");
                        compiledArtifacts.put(getUniqueName(slangModel), compiledSource);
                    } else {
                        loggingService.logEvent(Level.ERROR, "Failed to compile source: \'" +
                                slangModel.getNamespace() + "." + slangModel.getName() + "\'");
                    }
                }
            } catch (Exception e) {
                String errorMessage = "Failed compiling Slang source: \'" + slangModel.getNamespace() + "." +
                        slangModel.getName() + "\'.\n" + e.getMessage();
                loggingService.logEvent(Level.ERROR, errorMessage);
                compileResult.addException(new RuntimeException(errorMessage, e));
            }
        }

        compileResult.addResults(compiledArtifacts);
        return compileResult;
    }

    private Set<Executable> getModelDependenciesRecursively(Map<String, Executable> slangModels,
                                                            Executable slangModel) {
        Set<Executable> dependenciesModels = new HashSet<>();
        for (String dependencyName : slangModel.getExecutableDependencies()) {
            Executable dependency = slangModels.get(dependencyName);
            if (dependency == null) {
                throw new RuntimeException("Failed compiling slang source: " + slangModel.getNamespace() + "." +
                        slangModel.getName() + ". Missing dependency: " + dependencyName);
            }
            dependenciesModels.add(dependency);
            dependenciesModels.addAll(getModelDependenciesRecursively(slangModels, dependency));
        }
        return dependenciesModels;
    }

    private String getUniqueName(Executable sourceModel) {
        return sourceModel.getNamespace() + "." + sourceModel.getName();
    }

}

@@ -3202,59 +3202,8 @@
 %22%22;%0A
-        String errorMessagePrefixCompilation = %22%22;%0A
     
@@ -3458,32 +3458,39 @@
                 
+String 
 errorMessagePref

fix file name for builder - compile