package org.ethereumclassic.etherjar.contract;

import org.ethereumclassic.etherjar.model.HexData;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Igor Artamonov
 */
public class Compiler {

    private File solc;

    public Compiler(String solc) {
        this.solc = new File(solc);
    }

    public Result compile(File source, boolean optimize) throws IOException, InterruptedException {
        return compile(new FileInputStream(source), optimize);
    }

    public Result compile(String source, boolean optimize) throws IOException, InterruptedException {
        return compile(new ByteArrayInputStream(source.getBytes("UTF-8")), optimize);
    }

    //TODO support multiple sources
    public Result compile(InputStream source, boolean optimize) throws IOException, InterruptedException {
        Path tmp = Files.createTempDirectory("etherjar-compile");
        Path contractSource = Files.createTempFile(tmp, "contract", ".sol");
        Files.copy(source, contractSource, StandardCopyOption.REPLACE_EXISTING);

        List<String> args = new ArrayList<>();
        args.add(solc.getAbsolutePath());
        if (optimize) {
            args.add("--optimize");
        }
        args.add("--abi");
        args.add("--bin");
        args.add("--output-dir"); args.add("./");
        args.add("./" + contractSource.getFileName().toString());

        ProcessBuilder processBuilder = new ProcessBuilder(args)
            .directory(tmp.toFile());
        Process process = processBuilder.start();

        BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
        BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        String line;
        List<String> stdoutLines = new ArrayList<>();
        List<String> stderrLines = new ArrayList<>();
        while ((line = stdout.readLine()) != null) {
            stdoutLines.add(line);
        }
        while ((line = stderr.readLine()) != null) {
            stderrLines.add(line);
        }

        int status = process.waitFor();


        Stream<Path> bins = Files.list(tmp).filter((f) -> f.getFileName().toString().endsWith(".bin"));

        //TODO delete temp files after processing

        if (status != 0) {
            return new Result(false);
        }

        List<CompiledContract> contracts = bins.map((path -> {
            String name = path.getFileName().toString();
            name = name.substring(0, name.length() - ".bin".length());
            return name;
        })).map((name) -> {
            Path bin = tmp.resolve(name + ".bin");
            HexData binData = null;
            try {
                if (bin.toFile().length() > 0) {
                    List<String> data = Files.readAllLines(bin);
                    binData = HexData.from("0x" + data.get(0));
                }
            } catch (IOException e) {
                stderrLines.add(e.getMessage());
            }

            Path abi = tmp.resolve(name + ".abi");
            String json = null;
            try {
                json = String.join("", Files.readAllLines(abi));
            } catch (IOException e) {
                stderrLines.add(e.getMessage());
            }

            return new CompiledContract(name, binData, json);
        }).collect(Collectors.toList());

        return new Result(true).add(contracts);
    }

    public static class Result {
        private boolean success;
        private List<CompiledContract> contracts;

        public Result(boolean success) {
            this.success = success;
            contracts = new ArrayList<>();
        }

        public List<String> getNames() {
            return contracts.stream()
                .map(CompiledContract::getName)
                .collect(Collectors.toList());
        }

        public CompiledContract getContract(String name) {
            for(CompiledContract c: contracts) {
                if (c.getName().equals(name)) {
                    return c;
                }
            }
            return null;
        }

        public List<CompiledContract> getContracts() {
            return contracts;
        }

        public Result add(CompiledContract contract) {
            contracts.add(contract);
            return this;
        }
        public Result add(List<CompiledContract> contracts) {
            this.contracts.addAll(contracts);
            return this;
        }

        public int getCount() {
            return contracts.size();
        }
    }

    public static class CompiledContract {
        private final String name;
        private HexData compiled;
        private String abi;

        public CompiledContract(String name) {
            this.name = name;
        }

        public CompiledContract(String name, HexData compiled, String abi) {
            this(name);
            this.compiled = compiled;
            this.abi = abi;
        }

        public void setCompiled(HexData compiled) {
            this.compiled = compiled;
        }

        public void setAbi(String abi) {
            this.abi = abi;
        }

        public HexData getCompiled() {
            return compiled;
        }

        public String getAbi() {
            return abi;
        }

        public String getName() {
            return name;
        }
    }

}

@@ -107,24 +107,62 @@
  java.io.*;%0A
+import java.nio.file.FileVisitOption;%0A
 import java.
@@ -2285,165 +2285,8 @@
 );%0A%0A
-%0A        Stream%3CPath%3E bins = Files.list(tmp).filter((f) -%3E f.getFileName().toString().endsWith(%22.bin%22));%0A%0A        //TODO delete temp files after processing%0A%0A
     
@@ -2304,24 +2304,48 @@
 tus != 0) %7B%0A
+            clean(tmp);%0A
             
@@ -2366,24 +2366,24 @@
 ult(false);%0A
-
         %7D%0A%0A 
@@ -2373,32 +2373,137 @@
 se);%0A        %7D%0A%0A
+        Stream%3CPath%3E bins = Files.list(tmp).filter((f) -%3E f.getFileName().toString().endsWith(%22.bin%22));%0A%0A
         List%3CCom
@@ -3552,32 +3552,261 @@
 tracts);%0A    %7D%0A%0A
+    private void clean(Path tmp) throws IOException %7B%0A        Files.walk(tmp, FileVisitOption.FOLLOW_LINKS)%0A            .sorted(Comparator.reverseOrder())%0A            .map(Path::toFile)%0A            .forEach(File::delete);%0A    %7D%0A%0A
     public stati

delete transient files after compilation