/*
 * $Id$
 *
 * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.javatest.exec;

import com.sun.interview.Checklist;
import com.sun.interview.Interview;
import com.sun.interview.Question;
import com.sun.javatest.InterviewParameters;
import com.sun.javatest.TestEnvironment;
import com.sun.javatest.tool.FileChooser;
import com.sun.javatest.tool.ToolDialog;
import com.sun.javatest.tool.UIFactory;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;

class ChecklistBrowser extends ToolDialog {
    private static final String SAVE_AS = "SaveAs";
    private static final String PRINT_SETUP = "PrintSetup";
    private static final String PRINT = "Print";
    private ExecModel model;
    private InterviewParameters params;
    private MultiFormatPane body;
    private FileChooser fileChooser;
    private Listener listener;

    ChecklistBrowser(JComponent parent, ExecModel model, UIFactory uif) {
        super(parent, uif, "cb");

        if (model == null) {
            throw new NullPointerException();
        }

        this.model = model;

        params = model.getInterviewParameters();
        if (params == null) {
            throw new NullPointerException();
        }

        listener = new Listener();
    }

    @Override
    protected void initGUI() {
        setHelp("checklist.window.csh");

        JMenuBar mb = uif.createMenuBar("cb");
        String[] fileActions = {SAVE_AS, PRINT_SETUP, PRINT};
        JMenu fileMenu = uif.createMenu("cb.file", fileActions, listener);
        mb.add(fileMenu);
        setJMenuBar(mb);

        body = new MultiFormatPane(uif);
        body.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        uif.setAccessibleName(body, "cb");
        uif.setToolTip(body, "cb");
        int dpi = uif.getDotsPerInch();
        body.setPreferredSize(new Dimension(6 * dpi, 8 * dpi));
        setBody(body);

        JButton helpBtn = uif.createHelpButton("cb.help", "checklist.window.csh");
        JButton closeBtn = uif.createCloseButton("cb.close");
        setButtons(new JButton[]{helpBtn, closeBtn}, closeBtn);

        setComponentListener(listener);
    }

    private void updateContent() {
        if (body == null) {
            initGUI();
        }

        File f = params.getFile();
        if (f == null) {
            setI18NTitle("cb.title");
        } else {
            setI18NTitle("cb.titleWithFile", f);
        }

        try {
            Checklist c = params.createChecklist();
            StringWriter out = new StringWriter();
            writeChecklist(c, out);
        } catch (IOException e) {
            // should not happen while writing to StringWriter
        }

    }

    private void writeChecklist(Checklist c, Writer out) throws IOException {
        out.write("<!DOCTYPE HTML>\n");
        out.write("<html><body>\n");

        TestEnvironment env = params.getEnv();
        if (env != null) {
            // very improbable to have a checklist with no env!
            out.write("<h1>");
            out.write(escape(uif.getI18NString("cb.html.title", env.getName())));
            out.write("</h1>\n");
        }

        if (c.isEmpty()) {
            out.write(escape(uif.getI18NString("cb.html.noEntries")));
        } else {
            out.write(escape(uif.getI18NString("cb.html.intro")));
            String[] names = c.getSectionNames();
            for (String name : names) {
                writeSection(c, name, out);
            }
        }

        out.write("</body></html>");
        out.close();

        TextPane pane = (TextPane) body.getMediaPane(MultiFormatPane.TEXT);
        pane.showText(out.toString(), "text/html");
        pane.getCaret().setDot(0);
    }

    private void writeSection(Checklist c, String name, Writer out) throws IOException {
        out.write("<h2>");
        out.write(escape(name));
        out.write("</h2>\n");

        out.write("<ul>");
        String[] msgs = c.getSectionMessages(name);
        for (String msg : msgs) {
            out.write("<li>");
            out.write(escape(msg));
            out.write("\n");
        }
        out.write("</ul>\n");
    }

    private String escape(String s) {
        for (int i = 0; i < s.length(); i++) {
            switch (s.charAt(i)) {
                case '<':
                case '>':
                case '&':
                    StringBuilder sb = new StringBuilder(s.length() * 2);
                    for (int j = 0; j < s.length(); j++) {
                        char c = s.charAt(j);
                        switch (c) {
                            case '<':
                                sb.append("&lt;");
                                break;
                            case '>':
                                sb.append("&gt;");
                                break;
                            case '&':
                                sb.append("&amp;");
                                break;
                            default:
                                sb.append(c);
                        }
                    }
                    return sb.toString();
            }
        }
        return s;
    }

    private void doSaveAs() {
        if (fileChooser == null) {
            fileChooser = new FileChooser(true);
            fileChooser.addChoosableExtension(".html",
                    uif.getI18NString("cb.htmlFiles"));
        }

        fileChooser.setDialogTitle(uif.getI18NString("cb.save.title"));

        File file = null;

        while (file == null) {
            int rc = fileChooser.showDialog(parent, uif.getI18NString("cb.save.btn"));
            if (rc != JFileChooser.APPROVE_OPTION)
            // user has canceled or closed the chooser
            {
                return;
            }

            file = fileChooser.getSelectedFile();

            // if file exists, leave well enough alone;
            // otherwise, make sure it ends with .html
            if (!file.exists()) {
                String path = file.getPath();
                if (!path.endsWith(".html")) {
                    file = new File(path + ".html");
                }
            }

            // if file exists, make sure user wants to overwrite it
            if (file.exists()) {
                rc = uif.showYesNoCancelDialog("cb.save.warn");
                switch (rc) {
                    case JOptionPane.YES_OPTION:
                        break;  // use this file

                    case JOptionPane.NO_OPTION:
                        fileChooser.setSelectedFile(null);
                        file = null;
                        continue;  // choose another file

                    default:
                        return;  // exit without saving
                }
            }
        }

        try (Writer fw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8));
             Writer out = new BufferedWriter(fw)) {
            TextPane pane = (TextPane) body.getMediaPane(MultiFormatPane.TEXT);
            out.write(pane.getText());
        } catch (IOException e) {
            if (!file.canWrite()) {
                uif.showError("cb.save.cantWriteFile", file);
            } else if (e instanceof FileNotFoundException) {
                uif.showError("cb.save.cantFindFile", file);
            } else {
                uif.showError("cb.save.error", file, e);
            }
        }
    }

    private void doPrintSetup() {
        model.printSetup();
    }

    private void doPrint() {
        model.print(body);
    }

    private class Listener
            extends ComponentAdapter
            implements ActionListener, Interview.Observer {
        // ActionListener
        @Override
        public void actionPerformed(ActionEvent e) {
            String cmd = e.getActionCommand();
            if (cmd.equals(SAVE_AS)) {
                doSaveAs();
            } else if (cmd.equals(PRINT_SETUP)) {
                doPrintSetup();
            } else if (cmd.equals(PRINT)) {
                doPrint();
            }
        }

        // ComponentListener
        @Override
        public void componentShown(ComponentEvent e) {
            params.addObserver(this);
            updateContent();
        }

        @Override
        public void componentHidden(ComponentEvent e) {
            params.removeObserver(this);
        }

        // Interview.Observer
        @Override
        public void currentQuestionChanged(Question q) {
        }

        @Override
        public void pathUpdated() {
            updateContent();
        }

    }
}
