package net.rubygrapefruit.platform.prompts;

import net.rubygrapefruit.platform.terminal.TerminalInput;
import net.rubygrapefruit.platform.terminal.TerminalOutput;
import net.rubygrapefruit.platform.terminal.Terminals;

import java.util.List;

/**
 * Displays prompts on the terminal to ask the user various kinds of questions.
 */
public class Prompter {
    static final TerminalOutput.Color SELECTION_COLOR = TerminalOutput.Color.Cyan;
    static final TerminalOutput.Color DEFAULT_VALUE_COLOR = TerminalOutput.Color.White;
    static final TerminalOutput.Color INFO_COLOR = TerminalOutput.Color.White;
    private final TerminalOutput output;
    private final TerminalInput input;
    private final boolean interactive;

    public Prompter(Terminals terminals) {
        interactive = terminals.isTerminalInput() && terminals.isTerminal(Terminals.Output.Stdout) && terminals.getTerminal(Terminals.Output.Stdout).supportsCursorMotion();
        output = terminals.getTerminal(Terminals.Output.Stdout);
        input = terminals.getTerminalInput();
    }

    /**
     * Returns true if this prompter can ask the user questions.
     */
    public boolean isInteractive() {
        return interactive;
    }

    /**
     * Asks the user to select an option from a list.
     *
     * @return The index of the selected option or null on end of input. Returns the default option when not interactive.
     */
    public Integer select(String prompt, List<String> options, int defaultOption) {
        if (interactive) {
            return selectInteractive(prompt, options, defaultOption);
        } else {
            return defaultOption;
        }
    }

    /**
     * Asks the user to enter some text.
     *
     * @return The text or null on end of input. Returns the default value when not interactive.
     */
    public String enterText(String prompt, String defaultValue) {
        if (interactive) {
            return enterTextInteractive(prompt, defaultValue);
        } else {
            return defaultValue;
        }
    }

    /**
     * Asks the user to enter a password.
     *
     * @return The password or null on end of input or when not interactive.
     */
    public String enterPassword(String prompt) {
        if (interactive) {
            return enterPasswordInteractive(prompt);
        } else {
            return null;
        }
    }

    /**
     * Asks the user a yes/no question.
     *
     * @return The selected value or null on end of input. Returns the default value when not interactive.
     */
    public Boolean askYesNo(String prompt, boolean defaultValue) {
        if (interactive) {
            return yesNoInteractive(prompt, defaultValue);
        } else {
            return defaultValue;
        }
    }

    private Boolean yesNoInteractive(String prompt, boolean defaultValue) {
        YesNoView view = new YesNoView(output, prompt, defaultValue);
        YesNoListener listener = new YesNoListener(defaultValue);
        view.render();
        handleInput(listener);
        view.close(listener.getSelected());
        return listener.getSelected();
    }

    private Integer selectInteractive(String prompt, List<String> options, final int defaultOption) {
        SelectView view = new SelectView(output, prompt, options, defaultOption);
        SelectionListener listener = new SelectionListener(view, options);
        view.render();
        handleInput(listener);
        view.close(listener.getSelected());
        return listener.getSelected();
    }

    private String enterTextInteractive(String prompt, String defaultValue) {
        TextView view = new TextView(output, prompt, defaultValue);
        TextEntryListener listener = new TextEntryListener(view, defaultValue);
        view.render();
        handleInput(listener);
        view.close(listener.getEntered());
        return listener.getEntered();
    }

    private String enterPasswordInteractive(String prompt) {
        PasswordView view = new PasswordView(output, prompt);
        TextEntryListener listener = new TextEntryListener(view, null);
        view.render();
        handleInput(listener);
        view.close(listener.getEntered());
        return listener.getEntered();
    }

    private void handleInput(AbstractListener listener) {
        input.rawMode();
        try {
            while (!listener.isFinished()) {
                input.read(listener);
            }
        } finally {
            input.reset();
        }
    }
}

@@ -622,29 +622,27 @@
 nal 
-TerminalOutput output
+boolean interactive
 ;%0A  
@@ -669,58 +669,19 @@
 inal
-Input input;%0A    private final boolean interactive
+s terminals
 ;%0A%0A 
@@ -907,109 +907,34 @@
     
-output = terminals.getTerminal(Terminals.Output.Stdout);%0A        input = terminals.getTerminalInput()
+this.terminals = terminals
 ;%0A  
@@ -2711,32 +2711,112 @@
 defaultValue) %7B%0A
+        TerminalOutput output = terminals.getTerminal(Terminals.Output.Stdout);%0A
         YesNoVie
@@ -3173,32 +3173,112 @@
 efaultOption) %7B%0A
+        TerminalOutput output = terminals.getTerminal(Terminals.Output.Stdout);%0A
         SelectVi
@@ -3632,32 +3632,112 @@
 defaultValue) %7B%0A
+        TerminalOutput output = terminals.getTerminal(Terminals.Output.Stdout);%0A
         TextView
@@ -4063,32 +4063,112 @@
 tring prompt) %7B%0A
+        TerminalOutput output = terminals.getTerminal(Terminals.Output.Stdout);%0A
         Password
@@ -4419,32 +4419,32 @@
 tered();%0A    %7D%0A%0A
-
     private void
@@ -4481,24 +4481,84 @@
 listener) %7B%0A
+        TerminalInput input = terminals.getTerminalInput();%0A
         inpu

Fix `Prompter` so that it works when not connected to a terminal.