package org.kravemir.lightvalue;

import org.gradle.api.Task;
import org.gradle.api.tasks.*;
import org.kravemir.lightvalue.model.ModelFile;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class LightValueGeneratorTask extends SourceTask {
    private File outputDir;

    private boolean generateJacksonMixIns;

    @TaskAction
    public void generate() {
        prepareEmptyOutputDir();

        getSource().getFiles().forEach(this::generate);
    }

    private void generate(File inputFile) {
        Reader reader = null;
        Writer writer = null;
        try {
            reader = new BufferedReader(new FileReader(inputFile));
            writer = new BufferedWriter(new FileWriter(new File(getOutputDir(), inputFile.getName())));

            ModelLexer modelLexer = new ModelLexer(reader);

            List<Token> tokens = parseTokens(modelLexer);
            writer.write(tokens.toString());
            writer.write("\n");
            writer.flush();

            ModelFile modelFile = new ModelParser(tokens).parseFile();
            writer.write(modelFile.toString());
            writer.write("\n");

            ModelGenerator modelGenerator = new ModelGenerator();
            modelGenerator.generate(modelFile, getOutputDir());

            if(generateJacksonMixIns) {
                JacksonMixInsGenerator jacksonMixInsGenerator = new JacksonMixInsGenerator();
                jacksonMixInsGenerator.generate(modelFile, getOutputDir());
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            closeIfOpen(reader);
            closeIfOpen(writer);
        }
    }

    private List<Token> parseTokens(ModelLexer modelLexer) throws IOException {
        List<Token> tokens = new ArrayList<>();
        Token token;
        while ((token = modelLexer.yylex()) != null) {
            tokens.add(token);
        }
        return tokens;
    }

    private void closeIfOpen(Closeable closeable) {
        try {
            if (closeable != null) {
                closeable.close();
            }
        } catch (IOException e) {
            e.printStackTrace(System.err);
        }
    }

    private void prepareEmptyOutputDir() {
        if(getOutputDir().exists()) {
            getProject().delete(getOutputDir());
        }
        getProject().mkdir(getOutputDir());

    }

    @OutputDirectory
    public File getOutputDir() {
        return outputDir;
    }

    public void setOutputDir(final File outputDir) {
        this.outputDir = outputDir;
    }

    public void setOutputDir(final CharSequence outputDir) {
        this.outputDir = getProject().file(outputDir);
    }

    @Input
    public boolean getGenerateJacksonMixIns() {
        return generateJacksonMixIns;
    }

    public void setGenerateJacksonMixIns(boolean generateJacksonMixIns) {
        this.generateJacksonMixIns = generateJacksonMixIns;
    }

    public void registerInSourceSets(SourceSet... sourceSets) {
        for(SourceSet sourceSet : sourceSets) {
            sourceSet.getJava().srcDir(getOutputDir());
            Task compileJavaTask = getProject().getTasks().getByPath(sourceSet.getCompileJavaTaskName());
            compileJavaTask.dependsOn(this);
        }
    }
}
