package be.iminds.ilabt.jfed.experimenter_gui.bugreporting;

import be.iminds.ilabt.jfed.bugreport.BugReport;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import javafx.concurrent.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

/**
 * Writes a bugreport to a file
 * <p/>
 * User: twalcari
 * Date: 7/17/2014
 * Time: 14:22
 */
public class BugReportToFileTask extends Task<Integer> {
    private static final Logger LOG = LoggerFactory.getLogger(BugReportToFileTask.class);


    private final BugReportComposer bugReportComposer;

    /**
     * if outputfile is set, this task will save the output to this file. Otherwise, it will send it to the server
     */
    protected final File outputFile;

    public BugReportToFileTask(BugReportComposer bugReportComposer, File outputFile) {
        this.bugReportComposer = bugReportComposer;
        this.outputFile = outputFile;
    }


    @Override
    protected Integer call() throws Exception {
        if (!bugReportComposer.isPreparationFinished()) {
            updateMessage("Waiting for connectivity tests to finish");
            updateProgress(-1, 0);


            String dotsString = "";
            while (!bugReportComposer.isPreparationFinished()) {
                dotsString += ".";
                if (dotsString.length() > 3)
                    dotsString = ".";

                updateMessage("Waiting for connectivity tests to finish" + dotsString);
                Thread.sleep(800);
            }
        }



        updateMessage("Writing bug report to file");

        try (FileOutputStream fos = new FileOutputStream(outputFile)) {
            LOG.debug("Writing bug report to file {}", outputFile.getAbsolutePath());

            if (bugReportComposer.isFinished())
                bugReportComposer.reset();


            FutureTask<Void> writeToOutputStreamTask = new FutureTask<>(
                    () -> {
                        try {
                            BugReport bugReport = bugReportComposer.createBugReport();
                            ObjectMapper mapper = new ObjectMapper();
                            mapper.writerWithDefaultPrettyPrinter().writeValue(fos, bugReport);
                        } catch (IOException ex) {
                            throw new IOException("An error occurred while writing the bugreport");
                        }
                        return null;
                    }
            );

            ExecutorService es = Executors.newSingleThreadExecutor(
                    new ThreadFactoryBuilder().setNameFormat("BugReportToFile-%d").build());

            es.submit(writeToOutputStreamTask);
            es.shutdown();

            while (!es.isTerminated()) {
                updateProgress(Math.min(bugReportComposer.getProgress(), bugReportComposer.getTotalProgress()),
                        bugReportComposer.getTotalProgress());
            }

            //if the execution failed, the next statement will throw an executionException. This is what we want!
            Void aVoid = writeToOutputStreamTask.get();


            updateMessage("Writing bug report finished");

            LOG.debug("Successfully wrote bug report. Size={} KB", outputFile.length() / 1024.0);


//            return "Successfully saved bug report to " + this.outputFile.getAbsolutePath();
            return 0;
        }
    }

    @Override
    protected void failed() {
        super.failed();
        LOG.error("Writing bugreport to file {} failed: {}", outputFile.getAbsolutePath(),
                getException() != null ? getException().getMessage() : "no error",
                getException());

    }
}
