package cn.ibizlab.codegen;

import cn.ibizlab.codegen.ignore.CodegenIgnoreProcessor;
import cn.ibizlab.codegen.model.CliOption;
import cn.ibizlab.codegen.model.ModelStorage;
import cn.ibizlab.codegen.templating.*;
import cn.ibizlab.codegen.templating.TemplatePathLocator;
import cn.ibizlab.codegen.templating.TemplateProcessor;
import cn.ibizlab.codegen.templating.TemplatingEngineAdapter;
import cn.ibizlab.codegen.templating.mustache.*;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.file.Paths;
import java.util.*;

@Slf4j
@SuppressWarnings("rawtypes")
public class DefaultGenerator implements Generator {
    private static final String METADATA_DIR = ".ibizlab-generator";
    protected CodegenConfig config;

    private Map<String, String> generatorPropertyDefaults = new HashMap<>();
    protected TemplateProcessor templateProcessor = null;


    private CodegenIgnoreProcessor ignoreProcessor;


    protected ModelStorage modelStorage;


    @SuppressWarnings("deprecation")
    @Override
    public Generator opts(CodegenConfig opts) {
        this.config = opts;
        this.config.addLambda();

        modelStorage=ModelStorage.getInstance().config(config);

        TemplateManagerOptions templateManagerOptions = new TemplateManagerOptions(this.config.isEnableMinimalUpdate(),this.config.isSkipOverwrite());

        TemplatingEngineAdapter templatingEngine = this.config.getTemplatingEngine();

        TemplatePathLocator commonTemplateLocator = new CommonTemplateContentLocator();
        TemplatePathLocator generatorTemplateLocator = new GeneratorTemplateContentLocator(this.config);
        this.templateProcessor = new TemplateManager(
                templateManagerOptions,
                templatingEngine,
                new TemplatePathLocator[]{generatorTemplateLocator, commonTemplateLocator}
        );


        String ignoreFileLocation = this.config.getIgnoreFilePathOverride();

        this.ignoreProcessor=CodegenIgnoreProcessor.getInstance(ignoreFileLocation,this.config.getOutputDir(),this.config.getTemplateDir());

        return this;
    }

    @Override
    public List<File> generate() {

        for(TemplateDefinition templateDefinition:this.config.getTemplateDefinitions())
        {

            for(CliOption opt:templateDefinition.getTemplateDatas().getOptions())
            {
                try {
                    processTemplateToFile(opt,templateDefinition.getTemplateFile());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }


    public TemplateProcessor getTemplateProcessor() {
        return templateProcessor;
    }


    /**
     * Set generator properties otherwise pulled from system properties.
     * Useful for running tests in parallel without relying on System.properties.
     *
     * @param key   The system property key
     * @param value The system property value
     */
    @SuppressWarnings("WeakerAccess")
    public void setGeneratorPropertyDefault(final String key, final String value) {
        this.generatorPropertyDefaults.put(key, value);
    }

    private Boolean getGeneratorPropertyDefaultSwitch(final String key, final Boolean defaultValue) {
        String result = null;
        if (this.generatorPropertyDefaults.containsKey(key)) {
            result = this.generatorPropertyDefaults.get(key);
        }
        if (result != null) {
            return Boolean.valueOf(result);
        }
        return defaultValue;
    }



    protected File processTemplateToFile(Map<String, Object> templateData, String templateName) throws IOException {

        String outputFilename=this.templateProcessor.targetPath(templateData,templateName);

        File target = Paths.get(this.config.getOutputDir(),outputFilename).toFile();
        if (ignoreProcessor.allowsFile(target)) {

            return this.templateProcessor.write(templateData,templateName, target);

        } else {
            this.templateProcessor.ignore(target.toPath(), "Ignored by rule in ignore file.");
            return null;
        }
    }


}
