/*****************************************************************************
 *
 * CLIPP - command line interfaces for modern C++
 *
 * released under MIT license
 *
 * (c) 2017 André Müller; foss@andremueller-online.de
 *
 *****************************************************************************/

#include "testing.h"

using namespace clipp;

//-------------------------------------------------------------------
void test(int lineNo, const doc_formatting& fmt,
          const group& cli, const std::string& expected)
{
    auto us = usage_lines(cli,fmt).str();

//    std::cout << "\n======================================\n\n" << us << "\n";
//    return;

    if(us != expected) {
        std::cout << "\nEXPECTED:\n" << expected << "\nOBSERVED:\n" << us << '\n';

        throw std::runtime_error{"failed " + std::string( __FILE__ ) +
                                 " in line " + std::to_string(lineNo)};
    }
}


//-------------------------------------------------------------------
int main()
{
    try {

    auto fmt = doc_formatting{}
        .start_column(3)
        .doc_column(9)
        .empty_label("?")
        .param_separator("%")
        .group_separator("#")
        .alternative_param_separator("|")
        .alternative_group_separator("^")
        .flag_separator("_")
        .surround_labels("<", ">")
        .surround_optional("[", "]")
        .surround_repeat(",,,", "...")
        .surround_alternatives("§(§", "§)§")
        .surround_group("(", ")")
        .surround_joinable("$($", "$)$")
        .max_flags_per_param_in_doc(2)
        .max_flags_per_param_in_usage(1)
        .line_spacing(0)
        .paragraph_spacing(3)
        .alternatives_min_split_size(3)
        .merge_alternative_flags_with_common_prefix(true)
        .merge_joinable_with_common_prefix(true)
        .split_alternatives(true);

    test(__LINE__, fmt, group( option("-a") ), "   [-a]");
    test(__LINE__, fmt, group( option("-a", "--all") ), "   [-a]");
    test(__LINE__, fmt, group( option("-a").repeatable(true) ), "   ,,,[-a]...");

    test(__LINE__, fmt, group( required("-a") ), "   -a");
    test(__LINE__, fmt, group( required("-a", "--all") ), "   -a");
    test(__LINE__, fmt, group( required("-a").repeatable(true) ), "   ,,,-a...");

    test(__LINE__, fmt, group( command("-a") ), "   -a");
    test(__LINE__, fmt, group( command("-a", "--all") ), "   -a");
    test(__LINE__, fmt, group( command("-a").repeatable(true) ), "   ,,,-a...");


    test(__LINE__, fmt, (
            value("geometry file"),
            option("-translate") & value("x") & value("y") & value("z"),
            option("-rotate") & value("azimuth") & value("polar")),
         "   <geometry file>#[-translate%<x>%<y>%<z>]#[-rotate%<azimuth>%<polar>]");


    test(__LINE__, fmt, joinable( values("string")), "   ,,,<string>...");


    test(__LINE__, fmt, (
            (option("-o", "--out") & value("output file")) % "output filename",
            with_prefix("-f", option("align") >> []{} | option("noalign") >> []{} ) % "control alignment"),
         "   [-o%<output file>]#[-f§(§align|noalign§)§]");


    test(__LINE__, fmt, (
            (option("-n", "--count") & value("count")) % "number of iterations",
            (option("-r", "--ratio") & value("ratio")) % "compression ratio",
            (option("-m") & opt_value("lines=5")) % "merge lines (default: 5)"),
         "   [-n%<count>]#[-r%<ratio>]#[-m%[<lines=5>]]");


    test(__LINE__, fmt, (
            "input files" % values("file"),
            "compress results" % option("-c", "--compress"),
            "lines to be ignored" % repeatable( option("-i", "--ignore") & integers("line") )),
         "   ,,,<file>...#[-c]#,,,[-i%,,,<line>...]...");


    test(__LINE__, fmt, (
            value("file").if_missing([]{}).if_repeated([](int){}),
            required("-t") & values("target").if_missing([]{}).if_blocked([]{}),
            option("--http") | option("--ftp").if_conflicted([]{}),
            any_other()),
         "   <file>#-t%,,,<target>...#[--§(§http|ftp§)§]#,,,[<?>]...");


    test(__LINE__, fmt, (
            option("-r", "--recursive") % "recurse into subdirectories",
            (required("-i", "--in" ) & value("input dir")) % "sets path to input directory",
            (required("-o", "--out") & value("output dir")) % "sets path to output directory"),
         "   [-r]#-i%<input dir>#-o%<output dir>");


    test(__LINE__, fmt, (
            values("directory") % "input files",
            option("-c", "--compress") % "compress results",
            (option("-i", "--ignore") & values("line")) % "lines to be ignored"),
         "   ,,,<directory>...#[-c]#[-i%,,,<line>...]");


    test(__LINE__, fmt, (
            value("infile") % "input filename",
            value("outfile") % "output filename",
            option("-s", "--split") % "split files" ),
         "   <infile>%<outfile>%[-s]");


    test(__LINE__, fmt, (
            option("-a") % "activates a",
            option("-b") % "activates b",
            option("-c", "--noc") % "deactivates c",
            option("--hi")([]{}) % "says hi"),
         "   [-a]%[-b]%[-c]%[--hi]");


    test(__LINE__, fmt, (
            command("make"),
            value("file") % "name of file to make",
            option("-f", "--force") % "overwrite existing file"),
         "   make%<file>%[-f]");


    test(__LINE__, fmt, (
            option("-a") % "activates a",
            option("-b") % "activates b",
            option("-c", "--noc")   % "deactivates c",
            option("--hi")([]{}) % "says hi"),
         "   [-a]%[-b]%[-c]%[--hi]");


    test(__LINE__, fmt,
         joinable(repeatable( option(",") , opt_number("number"))),
         "   ,,,([,]%[<number>])...");


    test(__LINE__, fmt, (
            ( command("ship"), ( ( command("new"), values("name") ) |
                ( value("name"),
                    command("move"), value("x"), value("y"), (option("--speed=") & value("kn")) % "Speed in knots [default: 10]") |
                    ( command("shoot"), value("x"), value("y") ) ) )
                    | ( command("mine"),
                        (   command("set"   )
                            | command("remove") ),
                            value("x"), value("y"),
                            (   option("--moored"  ) % "Moored (anchored) mine."
                                | option("--drifting") % "Drifting mine.")
                    )
                    | command("-h", "--help")  % "Show this screen."
                    | command("--version")([]{}) % "Show version."),
         "   ship#new%,,,<name>...\n"
         "   ship#<name>#move#<x>#<y>#[--speed=%<kn>]\n"
         "   ship#shoot%<x>%<y>\n"
         "   mine#§(§set|remove§)§#<x>#<y>#[--§(§moored|drifting§)§]\n"
         "   -h\n"
         "   --version");


    test(__LINE__, fmt, ( command("help")
               | ( command("build"),
                   ( command("new") | command("add") ),
                   values("file"),
                   option("-v", "--verbose") % "print detailed report",
                   option("-b", "--buffer") & opt_value(
                       "size=10")  % "sets buffer size in KiByte",
                       ( option("--init") | option("--no-init") ) % "do or don't initialize"
               )
               | ( command("query"), value("infile"),
                   required("-o", "--out") & value("outfile"),
                   option("-f", "--out-format") % "determine output format"
                   & value("format")
               )),
       "   help\n"
       "   build#§(§new|add§)§#,,,<file>...#[-v]#[-b%[<size=10>]]#[--§(§init|no-init§)§]\n"
       "   query#<infile>#-o%<outfile>#[-f%<format>]");


    test(__LINE__, fmt, (
            value("file"),
            joinable(
                option("-r") % "open read-only",
                option("-b") % "use backup file",
                option("-s") % "use swap file"
            ),
            joinable(
                option(":vim") >> []{}, option(":st3") >> []{},
                option(":atom") >> []{}, option(":emacs") >> []{}
            ) % "editor(s) to use; multiple possible"),
         "   <file>#[-rbs]#$($[:vim]%[:st3]%[:atom]%[:emacs]$)$");


    test(__LINE__, fmt, (
            (option("x") % "sets X", option("y") % "sets Y"),
            (option("a") % "activates A", option("b") % "activates B") % "documented group 1:"       ,
            "documented group 2:" % (option("-g") % "activates G", option("-h") % "activates H"),
            "activates E or F" % (option("-e"), option("-f")))
         ,
         "   [x]#[y]#[a]%[b]#[-g]%[-h]#[-e]%[-f]");


    test(__LINE__, fmt, ( ( command("make"), value("wordfile"), required("-dict") & value("dictionary"), option("--progress", "-p")) | (
            command("find"), values("infile"), required("-dict") & value("dictionary"),
            (option("-o", "--output") & value("outfile"))  % "write to file instead of stdout",
            ( option("-split"  ) | option("-nosplit"))) | command("help"),
            option("-v", "--version").call([]{}) % "show version")
         ,
       "   make#<wordfile>#-dict%<dictionary>#[--progress]#[-v]\n"
       "   find#,,,<infile>...#-dict%<dictionary>#[-o%<outfile>]#[-§(§split|nosplit§)§]#[-v]\n"
       "   help#[-v]");


    test(__LINE__, fmt, (
            command("help") |
            ( command("build"),
                "build commands" %
                (   command("new") % "make new database"
                    | command("add") % "append to existing database"
                ),
                value("file")
            ) |
            ( command("query"),
                "query settings" %
                (   required("-i", "--input") & value("infile") % "input file",
                    option("-p", "--pretty-print") % "human friendly output")
            ) |
            ( command("info"),
                "database info modes" % (
                    command("space") % "detailed memory occupation analysis" |
                    (
                        command("statistics"),
                        "statistics analysis" % (
                            command("words") % "word frequency table" |
                            command("chars") % "character frequency table"
                        )
                    )
                )
            ) |
            "remove mode" % (
                command("remove"),
                "modify" % ( command("any") | command("all") ),
                value("regex") % "regular expression filter"
            ) |
            ( command("modify"),
                "modification opererations" % (
                    option("-c", "--compress") % "compress database in-memory",
                    option("-u", "--unique") % "keep only unique entries",
                    option("-m", "--memlimit") % "max. size in RAM" & value("size")
                )
            )
        ),
        "   help\n"
        "   build#§(§new|add§)§#<file>\n"
        "   query#-i%<infile>#[-p]\n"
        "   info#space\n"
        "   info#statistics#§(§words|chars§)§\n"
        "   remove#a§(§ny|ll§)§#<regex>\n"
        "   modify#[-c]#[-u]#[-m%<size>]");


    test(__LINE__, fmt, (
            value("input file"),
            option("-r", "--recursive"),
            option("-o") & value("output format"),
            option("-utf16")),
         "   <input file>#[-r]#[-o%<output format>]#[-utf16]");


    test(__LINE__, fmt,
         joinable( repeatable(option("a")([]{})|option("b")([]{})) ),
         "   ,,,[a|b]...");


    test(__LINE__, fmt, (
            "user interface options:" % (
                option("-v", "--verbose") % "show detailed output",
                option("-i", "--interactive") % "use interactive mode"
            ),
            "copy mode:" % (
                command("copy") | command("move"),
                option("--all") % "copy all",
                option("--replace") % "replace existing files",
                option("-f", "--force") % "don't ask for confirmation"
            ) |
            "compare mode:" % (
                command("compare"),
                (command("date") | command("content")),
                option("-b", "--binary") % "compare files byte by byte",
                option("-q", "--quick") % "use heuristics for faster comparison"
            ) |
            "merge mode:" % (
                command("merge"),
                (
                    command("diff") % "merge using diff"  |
                    command("patch") % "merge using patch" |
                    (   command("content") % "merge based on content",
                        "content based merge options:" % (
                            option("--git-style") % "emulate git's merge behavior",
                            option("-m", "--marker") & value("marker") % "merge marker symbol"
                        )
                    )
                ),
                required("-o") & value("outdir") % "target directory for merge result",
                option("--show-conflicts") % "show merge conflicts during run"
            ) |
            command("list"),
            "mode-independent options:" % (
                values("files") % "input files",
                option("-r", "--recursive") % "descend into subdirectories",
                option("-h", "--help") % "show help"
            )
        ),
         "   [-v]%[-i]#§(§copy|move§)§#[--all]#[--replace]#[-f]#,,,<files>...%[-r]%[-h]\n"
         "   [-v]%[-i]#compare#§(§date|content§)§#[-b]#[-q]#,,,<files>...%[-r]%[-h]\n"
         "   [-v]%[-i]#merge#§(§diff|patch§)§#-o%<outdir>#[--show-conflicts]#,,,<files>...%[-r]%[-h]\n"
         "   [-v]%[-i]#merge#content#[--git-style]#[-m%<marker>]#-o%<outdir>#[--show-conflicts]#,,,<files>...%[-r]%[-h]\n"
         "   [-v]%[-i]#list#,,,<files>...%[-r]%[-h]");


    test(__LINE__, fmt, (
            command("new"),
            value("filename"),
            (option("-e", "--encoding") & value("enc")).doc("'utf8' or 'cp1252'")
        ), "   new#<filename>#[-e%<enc>]");


    test(__LINE__, fmt, ( command("auto") | ( command("label"), value("character") )),
         "   §(§auto^(label%<character>)§)§");


    test(__LINE__, fmt, (
            values("file") % "input filenames",
            (required("-s") & value("expr")) % "string to look for",
            option("any") % "report as soon as any matches" |
            option("all") % "report only if all match"
        ),
        "   ,,,<file>...#-s%<expr>#[a§(§ny|ll§)§]");


    test(__LINE__, fmt, ( value(""), option("-x"), option("-y") ),
         "   <?>%[-x]%[-y]");

    }
    catch(std::exception& e) {
        std::cerr << e.what() << std::endl;
        return 1;
    }
}
