package environment;

import java.io.InputStream;
import java.io.PrintStream;
import java.util.function.Function;

import error.ErrorManager;

public class RuntimeEnvironment {

    private static RuntimeEnvironment uniqueInstance = new RuntimeEnvironment();

    public static RuntimeEnvironment getInstance() {
        return uniqueInstance;
    }

    private String inputName;
    private InputStream input;
    private PrintStream output;
    private PrintStream errorOutput;
    private ErrorManager errorManager;
    private String path;
    private Runnable terminationFunction;
    private Runnable errorTerminationFunction;
    private Function<String, String> promptDecorator;
    private Function<String, String> valueOutputDecorator;
    private Function<String, String> warningOutputDecorator;
    private Function<String, String> errorOutputDecorator;
    private Function<String, String> criticalOutputDecorator;

    private RuntimeEnvironment() {
        reset();
    }

    public void reset() {
        inputName = null;
        input = null;
        output = null;
        errorOutput = null;
        errorManager = null;
        path = null;
        terminationFunction = null;
        errorTerminationFunction = null;
        promptDecorator = null;
        valueOutputDecorator = null;
        warningOutputDecorator = null;
        errorOutputDecorator = null;
        criticalOutputDecorator = null;
    }

    public void setInputName(String inputName) {
        this.inputName = inputName;
    }

    public void setInput(InputStream input) {
        this.input = input;
    }

    public void setOutput(PrintStream output) {
        this.output = output;
    }

    public void setErrorOutput(PrintStream errorOutput) {
        this.errorOutput = errorOutput;
    }

    public void setErrorManager(ErrorManager errorManager) {
        this.errorManager = errorManager;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public void setTerminationFunction(Runnable terminationFunction) {
        this.terminationFunction = terminationFunction;
    }

    public void setErrorTerminationFunction(Runnable errorTerminationFunction) {
        this.errorTerminationFunction = errorTerminationFunction;
    }

    public void setPromptDecorator(Function<String, String> promptDecorator) {
        this.promptDecorator = promptDecorator;
    }

    public void setValueOutputDecorator(Function<String, String> valueOutputDecorator) {
        this.valueOutputDecorator = valueOutputDecorator;
    }

    public void setWarningOutputDecorator(Function<String, String> warningOutputDecorator) {
        this.warningOutputDecorator = warningOutputDecorator;
    }

    public void setErrorOutputDecorator(Function<String, String> errorOutputDecorator) {
        this.errorOutputDecorator = errorOutputDecorator;
    }

    public void setCriticalOutputDecorator(Function<String, String> criticalOutputDecorator) {
        this.criticalOutputDecorator = criticalOutputDecorator;
    }

    public String getInputName() {
        return inputName;
    }

    public InputStream getInput() {
        return input;
    }

    public PrintStream getOutput() {
        return output;
    }

    public PrintStream getErrorOutput() {
        return errorOutput;
    }

    public ErrorManager getErrorManager() {
        return errorManager;
    }

    public String getPath() {
        return path;
    }

    public void terminateSuccessfully() {
        terminationFunction.run();
    }

    public void terminateExceptionally() {
        errorTerminationFunction.run();
    }

    public String decoratePrompt(String prompt) {
        return promptDecorator.apply(prompt);
    }

    public String decorateValueOutput(String valueOutput) {
        return valueOutputDecorator.apply(valueOutput);
    }

    public String decorateWarningOutput(String warningOutput) {
        return warningOutputDecorator.apply(warningOutput);
    }

    public String decorateErrorOutput(String errorOutput) {
        return errorOutputDecorator.apply(errorOutput);
    }

    public String decorateCriticalOutput(String criticalOutput) {
        return criticalOutputDecorator.apply(criticalOutput);
    }

}
