package error;

import static error.ErrorManager.Severity.CRITICAL;
import static error.ErrorManager.Severity.WARNING;
import static java.text.MessageFormat.format;

import java.io.PrintStream;

import environment.RuntimeEnvironment;

/**
 * Prints error messages and potentially terminates the application.
 */
public class ErrorManager {

    private RuntimeEnvironment environment;

    public ErrorManager() {
        this.environment = RuntimeEnvironment.getInstance();
    }

    public void handle(LispException lispException) {
        printMessage(lispException);

        if (isCritical(lispException))
            environment.terminateExceptionally();
    }

    private void printMessage(LispException lispException) {
        PrintStream output = selectOutputStream(lispException.getSeverity());
        output.println(formatMessage(lispException));
    }

    private PrintStream selectOutputStream(Severity severity) {
        if (severity == WARNING)
            return environment.getOutput();

        return environment.getErrorOutput();
    }

    private String formatMessage(LispException lispException) {
        Severity severity = lispException.getSeverity();
        String prefix = severity.toDisplayString();
        String message = format("[{0}] {1}", prefix, lispException.getMessage());

        return severity.decorate(message, environment);
    }

    private boolean isCritical(LispException lispException) {
        return lispException.getSeverity() == CRITICAL;
    }

    public static enum Severity {

        WARNING {

            @Override
            public String decorate(String warningOutput, RuntimeEnvironment environment) {
                return environment.decorateWarningOutput(warningOutput);
            }

            @Override
            public String toDisplayString() {
                return "warning";
            }
        },

        ERROR {

            @Override
            public String decorate(String errorOutput, RuntimeEnvironment environment) {
                return environment.decorateErrorOutput(errorOutput);
            }

            @Override
            public String toDisplayString() {
                return "error";
            }
        },

        CRITICAL {

            @Override
            public String decorate(String criticalOutput, RuntimeEnvironment environment) {
                return environment.decorateCriticalOutput(criticalOutput);
            }

            @Override
            public String toDisplayString() {
                return "critical";
            }
        };

        public abstract String decorate(String output, RuntimeEnvironment environment);

        public abstract String toDisplayString();
    }

}
