import ceylon.formatter.options {
    SparseFormattingOptions,
    LineBreakStrategy,
    IndentMode
}
import ceylon.language.meta {
    annotations
}
import ceylon.language.meta.model {
    Type,
    UnionType,
    ClassOrInterface
}

"Returns a help string for the given [[topic]],
 or [[null]] if no help is available for this topic."
shared String? help(topic) {
    "The requested help topic, or [[null]] for general help.
     
     Currently available:
     - `options`: A document list of all [[formatting options|ceylon.formatter.options::FormattingOptions]]."
    String? topic;
    
    switch (topic)
    case (null) {
        return
            "ceylon.formatter – a Ceylon module / program to format Ceylon source code.
             
             USAGE
             
                 ceylon run ceylon.formatter source
             
             or, if you’re worried about it breaking your source code (which shouldn’t happen –
             if anything bad happens, error recovery kicks in and the original file is restored)
             or you just want to test it out:
             
                 ceylon run ceylon.formatter source --to source-formatted
             
             You can also format multiple folders at the same time:
             
                 ceylon run ceylon.formatter source --and test-source --to formatted
             
             which will recreate the ‘source’ and ‘test-source’ folders inside the new ‘formatted’ folder.
             
             OPTIONS
             
             --help
                 Print this help message.
                 (--help=options prints help for the various options.)
             
             --version
                 Print version information. The first line is always just the module name and version
                 in the format that ‘ceylon run’ understands (“ceylon.formatter/x.y.z”), which might be
                 useful for scripts.
             
             --${option name}=${option value}
                 Set a formatting option. The most useful ones are:
                 
                 --maxLineLength
                     The maximum line length, or “unlimited”.
                 
                 --indentMode
                     The indentation mode. Syntax: “x spaces” or “y-wide tabs” or “mix x-wide tabs, y spaces”.
                 
                 --lineBreak
                     “lf”, “crlf”, or “os” for the operating system’s native line breaks.
                 
                 For a full list of options, see the output from ‘--help=options’
                 or the documentation of the FormattingOptions class.";
    }
    case ("options") {
        StringBuilder ret = StringBuilder();
        ret.append("The following options are available:");
        for (option in `SparseFormattingOptions`.getDeclaredAttributes<SparseFormattingOptions,Anything,Nothing>()) {
            value optionDec = option.declaration;
            ret.append("\n\n");
            ret.append(optionDec.name);
            ret.append(": ");
            ret.append(formatType(option.type));
            `SparseFormattingOptions`.getDeclaredAttributes();
            for (line in annotations(`DocAnnotation`, optionDec)?.description?.lines else []) {
                ret.appendNewline();
                ret.append("    ");
                ret.append(line);
            }
        }
        return ret.string;
    }
    else {
        return null;
    }
}

String formatType(Type<Anything> type) {
    assert (nonempty partialTypes = collectPartialTypes(type));
    StringBuilder ret = StringBuilder();
    ret.append(partialTypes.first);
    for (partialType in partialTypes.rest) {
        if (partialType == partialTypes.last) {
            ret.append(" or ");
        } else {
            ret.append(", ");
        }
        ret.append(partialType);
    }
    return ret.string;
}

String[] collectPartialTypes(Type<Anything> type) {
    if (is UnionType<Anything> type) {
        return concatenate(*type.caseTypes.collect(collectPartialTypes));
    } else if (type.exactly(`Integer`)) {
        return ["Integer"];
    } else if (type.exactly(`Range<Integer>`)) {
        return ["Range<Integer> (‘x..y’)"];
    } else if (type.exactly(`IndentMode`)) {
        return ["IndentMode (‘x spaces’ or ‘y-wide tabs’ or ‘mix x-wide tabs, y spaces’)"];
    } else if (type.exactly(`LineBreakStrategy`)) {
        return ["LineBreakStrategy (‘default’)"];
    } else if (type.exactly(`{String*}`)) {
        return ["{String*} (‘abc def etc’)"];
    } else {
        assert (is ClassOrInterface<Anything> type);
        return type.caseValues.narrow<Object>().map((e) => "‘``e.string``’").sequence();
    }
}