/**
 * Copyright (c) 2004-2005, Regents of the University of California
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the University of California, Los Angeles nor the
 * names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package cck.help;

import cck.text.StringUtil;
import cck.text.Terminal;
import cck.util.*;

import java.util.*;

/**
 * The <code>HelpCategory</code> class implements a category that provides help from the
 * command line. It can contain multiple sections, including sections on options, and a list
 * of other help items (such as what the values of each option does).
 *
 * @author Ben L. Titzer
 */
public class HelpCategory implements HelpItem {

    /**
     * The <code>options</code> field stores a reference to an instance of the <code>Options</code> class that
     * encapsulates the command line options available to this action.
     */
    public final Options options = new Options();

    public String name;
    public final String help;

    private final LinkedList<Section> sections;

    public static final Comparator<HelpCategory> COMPARATOR = new Comparator<HelpCategory>() {
        public int compare(HelpCategory c1, HelpCategory c2) {
            return String.CASE_INSENSITIVE_ORDER.compare(c1.name, c2.name);
        }
    };

    /**
     * The <code>newOption()</code> is used by subclasses to easily create new options for this action.
     *
     * @param name the name of the option
     * @param val  the default value of the option
     * @param desc a string representation of the help for this option
     * @return an instance of <code>Option</code> representing the option created and registered
     */
    protected Option.Bool newOption(String name, boolean val, String desc) {
        return options.newOption(name, val, desc);
    }

    /**
     * The <code>newOption()</code> is used by subclasses to easily create new options for this action.
     *
     * @param name the name of the option
     * @param val  the default value of the option
     * @param desc a string representation of the help for this option
     * @return an instance of <code>Option</code> representing the option created and registered
     */
    protected Option.Long newOption(String name, long val, String desc) {
        return options.newOption(name, val, desc);
    }

    /**
     * The <code>newOption()</code> is used by subclasses to easily create new options for this action.
     *
     * @param name the name of the option
     * @param val  the default value of the option
     * @param desc a string representation of the help for this option
     * @return an instance of <code>Option</code> representing the option created and registered
     */
    protected Option.Double newOption(String name, double val, String desc) {
        return options.newOption(name, val, desc);
    }

    /**
     * The <code>newOption()</code> is used by subclasses to easily create new options for this action. This
     * particular method creates an interval option.
     *
     * @param name the name of the option
     * @param l    the default low value of the option
     * @param h    the default high value of the option
     * @param desc a string representation of the help for this option
     * @return an instance of <code>Option</code> representing the option created and registered
     */
    protected Option.Interval newOption(String name, long l, long h, String desc) {
        return options.newOption(name, l, h, desc);
    }

    /**
     * The <code>newOption()</code> is used by subclasses to easily create new options for this action.
     *
     * @param name the name of the option
     * @param val  the default value of the option as a string
     * @param desc a string representation of the help for this option
     * @return an instance of <code>Option</code> representing the option created and registered
     */
    protected Option.Str newOption(String name, String val, String desc) {
        return options.newOption(name, val, desc);
    }

    /**
     * The <code>newOptionList()</code> is used by subclasses to easily create new options for this action.
     *
     * @param name the name of the option
     * @param val  the default value of the option as a comma separated string
     * @param desc a string representation of the help for this option
     * @return an instance of <code>Option</code> representing the option created and registered
     */
    protected Option.List newOptionList(String name, String val, String desc) {
        return options.newOptionList(name, val, desc);
    }

    private abstract class Section {
        abstract void printHelp();
    }

    private class ParagraphSection extends Section {
        final String title;
        final String para;

        ParagraphSection(String t, String p) {
            title = t;
            para = p;
        }

        void printHelp() {
            if (title != null) {
                Terminal.printBrightBlue(title);
                Terminal.println("\n");
            }

            Terminal.println(StringUtil.formatParagraphs(para, 0, 4, Terminal.MAXLINE));
            Terminal.println("");
        }
    }

    private class OptionsSection extends Section {
        final String para;
        final Options options;

        OptionsSection(String para, Options opts) {
            this.para = para;
            this.options = opts;
        }

        void printHelp() {
            Terminal.printBrightBlue("OPTIONS");
            Terminal.println("\n");

            Terminal.println(StringUtil.formatParagraphs(para, 0, 4, Terminal.MAXLINE));
            Terminal.println("");

            Collection<Option> c = options.getAllOptions();
            List<Option> l = Collections.list(Collections.enumeration(c));
            Collections.sort(l, Option.COMPARATOR);

            for (Option opt : l) {
                opt.printHelp();
            }

            Terminal.println("");
        }
    }

    private class ListSection extends Section {
        final String title;
        final String para;
        final List<HelpItem> list;

        ListSection(String t, String p, List<HelpItem> l) {
            title = t;
            para = p;
            list = l;
        }

        void printHelp() {
            if (title != null) {
                Terminal.printBrightBlue(title);
                Terminal.println("\n");
            }

            Terminal.println(StringUtil.formatParagraphs(para, 0, 4, Terminal.MAXLINE));
            Terminal.println("");

            for (HelpItem hi : list) {
                hi.printHelp();
            }

            Terminal.println("");
        }
    }

    /**
     * The constructor for the <code>HelpCategory</code> class creates a new help category with the specified
     * short name and the specified default help.
     *
     * @param name the short name of this category (where it is accessible from the command line)
     * @param help the help description for this category
     */
    public HelpCategory(String name, String help) {
        this.name = name;
        this.help = help;
        this.sections = new LinkedList<Section>();
    }

    /**
     * The <code>getHelp()</code> method returns a string representing help for this help item.
     *
     * @return a help string for this item
     */
    public String getHelp() {
        return help;
    }

    /**
     * The <code>getName()</code> method returns the short name for this help category. This short name
     * is used to add this help category to the global help category database.
     *
     * @return the short name of this help category
     */
    public String getName() {
        return name;
    }

    /**
     * The <code>setName()</code> method is used to set the short name for this help category.
     *
     * @param nm the new short name for this category
     */
    public void setName(String nm) {
        name = nm;
    }

    /**
     * The <code>addSection()</code> method adds a new section to this help category with the specified title
     * and a paragraph that is automatically formatted when printed out.
     *
     * @param title     the title of the new section
     * @param paragraph a long string representing the text for this section
     */
    public void addSection(String title, String paragraph) {
        sections.addLast(new ParagraphSection(title, paragraph));
    }

    /**
     * The <code>addOptionSection()</code> method adds a new section to this help category with the specified
     * options. The new section will contain a paragraph description of the options and list the options in
     * alphabetical order.
     *
     * @param para a summary of the options
     * @param opts the options for this help category
     */
    public void addOptionSection(String para, Options opts) {
        sections.addLast(new OptionsSection(para, opts));
    }

    /**
     * The <code>addListSection()</code> method adds a new section to this help category with the specified
     * list of help items.
     *
     * @param title the title of the new sectiobn
     * @param para  a paragraph description of the section
     * @param l     a list of <code>HelpItem</code> instances that will be added to the end of the section
     */
    public void addListSection(String title, String para, List<HelpItem> l) {
        sections.addLast(new ListSection(title, para, l));
    }

    /**
     * The <code>addSubcategorySection</code> method adds a new section that is a list of subcategories
     * under this main category.
     *
     * @param title the title of the section
     * @param para  a paragraph description of this section
     * @param l     a list of subcategories
     */
    public void addSubcategorySection(String title, String para, List<HelpCategory> l) {
        LinkedList<HelpItem> sl = new LinkedList<HelpItem>();
        for (HelpCategory hc : l) {
            sl.addLast(new SubcategoryItem(4, hc));
        }
        addListSection(title, para, sl);
    }

    public void addOptionValueSection(String title, String para, String optname, ClassMap optvals) {
        LinkedList<HelpItem> list = new LinkedList<HelpItem>();
        for (String s : optvals.getSortedList()) {
            list.addLast(new ClassMapValueItem(4, optname, s, optvals));
        }

        addListSection(title, para, list);
    }

    /**
     * The <code>addCommandExample()</code> method adds a command example, properly formatted, to
     * this section.
     *
     * @param prefix  a string that is the prefix to the command line
     * @param command the command line example to add
     */
    public void addCommandExample(String prefix, String command) {
        throw Util.unimplemented();
    }

    /**
     * The <code>printHelp()</code> method prints out all of the help sections in order for this category.
     */
    public void printHelp() {
        for (Section s : sections) {
            s.printHelp();
        }
    }
}
