/* Copyright (C) 2003-2007  The Jmol Development Team
 *
 * Contact: cdk-devel@lists.sourceforge.net
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.openscience.cdk.io.listener;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.List;

import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.io.ReaderEvent;
import org.openscience.cdk.io.setting.BooleanIOSetting;
import org.openscience.cdk.io.setting.IOSetting;
import org.openscience.cdk.io.setting.IOSetting.Importance;
import org.openscience.cdk.io.setting.OptionIOSetting;

/**
 * Allows processing of IOSetting quesions which are passed to the user
 * by using the System.out and System.in by default.
 *
 * <p>This listener can also be used to list all the questions a ChemObjectWriter
 * has, by using a dummy StringWriter, and a <code>null</code> Reader.
 *
 * @cdk.module io
 * @cdk.githash
 *
 * @author Egon Willighagen &lt;egonw@sci.kun.nl&gt;
 */
public class TextGUIListener implements IReaderListener, IWriterListener {

    private BufferedReader in;
    private PrintWriter    out;

    private Importance     level = Importance.HIGH;

    public TextGUIListener(Importance level) {
        this.level = level;
        this.setInputReader(new InputStreamReader(System.in));
        this.setOutputWriter(new OutputStreamWriter(System.out));
    }

    public void setLevel(Importance level) {
        this.level = level;
    }

    /**
     * Overwrites the default writer to which the output is directed.
     */
    public void setOutputWriter(Writer writer) {
        if (writer instanceof PrintWriter) {
            this.out = (PrintWriter) writer;
        } else if (writer == null) {
            this.out = null;
        } else {
            this.out = new PrintWriter(writer);
        }
    }

    /**
     * Overwrites the default reader from which the input is taken.
     */
    public void setInputReader(Reader reader) {
        if (reader instanceof BufferedReader) {
            this.in = (BufferedReader) reader;
        } else if (reader == null) {
            this.in = null;
        } else {
            this.in = new BufferedReader(reader);
        }
    }

    @Override
    public void frameRead(ReaderEvent event) {}

    /**
     * Processes the IOSettings by listing the question, giving the options
     * and asking the user to provide their choice.
     *
     * <p>Note: if the input reader is <code>null</code>, then the method
     * does not wait for an answer, and takes the default.
     */
    @Override
    public void processIOSettingQuestion(IOSetting setting) {
        // post the question
        if (setting.getLevel().ordinal() <= this.level.ordinal()) {
            // output the option name
            this.out.print("[" + setting.getName() + "]: ");
            // post the question
            this.out.print(setting.getQuestion());
            if (setting instanceof BooleanIOSetting) {
                BooleanIOSetting boolSet = (BooleanIOSetting) setting;
                boolean set = boolSet.isSet();
                if (set) {
                    this.out.print(" [Yn]");
                } else {
                    this.out.print(" [yN]");
                }
            } else if (setting instanceof OptionIOSetting) {
                OptionIOSetting optionSet = (OptionIOSetting) setting;
                List<String> settings = optionSet.getOptions();
                for (int i = 0; i < settings.size(); i++) {
                    this.out.println();
                    String option = (String) settings.get(i);
                    this.out.print((i + 1) + ". " + option);
                    if (option.equals(setting.getSetting())) {
                        this.out.print(" (Default)");
                    }
                }
            } else {
                this.out.print(" [" + setting.getSetting() + "]");
            }
            this.out.println();
            this.out.flush();

            // get the answer, only if input != null
            if (this.in == null) {
                // don't really ask questions. This is intentional behaviour to
                // allow for listing all questions. The settings is now defaulted,
                // which is the intention too.
            } else {
                boolean gotAnswer = false;
                while (!gotAnswer) {
                    try {
                        this.out.print("> ");
                        this.out.flush();
                        String answer = in.readLine();
                        if (answer.length() == 0) {
                            // pressed ENTER -> take default
                        } else if (setting instanceof OptionIOSetting) {
                            ((OptionIOSetting) setting).setSetting(Integer.parseInt(answer));
                        } else if (setting instanceof BooleanIOSetting) {
                            if (answer.equalsIgnoreCase("n") || answer.equalsIgnoreCase("no")) {
                                answer = "false";
                            }
                            if (answer.equalsIgnoreCase("y") || answer.equalsIgnoreCase("yes")) {
                                answer = "true";
                            }
                            setting.setSetting(answer);
                        } else {
                            setting.setSetting(answer);
                        }
                        gotAnswer = true;
                    } catch (IOException exception) {
                        this.out.println("Cannot read from STDIN. Skipping question.");
                    } catch (NumberFormatException exception) {
                        this.out.println("Answer is not a number.");
                    } catch (CDKException exception) {
                        this.out.println();
                        this.out.println(exception.toString());
                    }
                }
            }
        }
    }

}
