# SPDX-FileCopyrightText: 2025 Florent Rougon
# SPDX-License-Identifier: GPL-2.0-or-later

"""Commands for checking XML PropertyList files and directory trees.

Some of these commands are suitable as pre-commit hooks.

"""

import argparse
import locale
import os
import sys

import flightgear.meta.sgprops_dircheckers as dircheckers

PROGNAME = os.path.basename(sys.argv[0])


def verboseLevelFromParams(params):
    if params.quiet:
        return dircheckers.VerboseLevel.QUIET
    elif params.verbose:
        return dircheckers.VerboseLevel.HIGH
    else:
        return dircheckers.VerboseLevel.DEFAULT


def checkXMLFilesFunction(params):
    """Implementation of the 'check-files' subcommand."""
    verboseLevel = verboseLevelFromParams(params)
    checker = dircheckers.CheckerBase(verboseLevel)
    res = 0                     # no check failed, so far

    for file_ in params.files:
        if not checker.checkXMLFile(file_, params.include):
            res = 1             # failed check

    checker.printErrors()
    return res


def recursiveDirCheckFunction(params):
    """Implementation of the 'check-dirs' subcommand."""
    verboseLevel = verboseLevelFromParams(params)
    checker = dircheckers.SimpleDirectoryChecker(verboseLevel)

    return checker.checkAll(params.directories, params.include)


def specialFGDataCheckFunction(params):
    """Implementation of the 'check-fgdata' subcommand."""
    verboseLevel = verboseLevelFromParams(params)
    checker = dircheckers.FGDataDirectoryChecker(verboseLevel)

    return checker.checkAll(params.directories, params.include)


def fgdataPrecommitHookFunction(params):
    """Implementation of the 'fgdata-precommit-hook' subcommand."""
    root = os.getcwd()          # must be the root of an FGData directory tree
    verboseLevel = verboseLevelFromParams(params)
    checker = dircheckers.FGDataFileSetChecker(verboseLevel)

    return checker.check(root, params.files, params.include)


def processCommandLine():
    parser = argparse.ArgumentParser(
# Commented out because this doesn't work well for “PROGNAME SUBCOMMAND --help”
#         usage="""\
# %(prog)s [OPTION ...] SUBCOMMAND FILE_OR_DIRECTORY...
# Validate XML files that appear to be in PropertyList format.""",
        description=f"""\
Validate XML files that appear to be in PropertyList format.

Examples:

  $ %(prog)s check-files file1.xml file2.xml

  The files must be in XML format. Any of the specified files which
  appears to be in PropertyList format is checked.

  $ %(prog)s check-dirs directory1 directory2

  The directories are recursively explored. During this exploration, any
  file found whose extension is 'xml' (ignoring the case) and which
  appears to be in PropertyList format is checked. In this mode, each of
  the explored directories is prepended to the list of include paths while
  it is being explored (except if it is already present due to the use of
  --include or --prepend-curdir-to-includes). This automatic addition has
  higher precedence than that resulting from --prepend-curdir-to-includes.

  $ %(prog)s -I/path/to/fgdata check-dirs /path/to/f-14b /path/to/other...

  This checks all PropertyList files in the specified aircraft
  directories (all the hierarchy, and this not limited to files
  reachable from aircraft -set.xml files).

  $ %(prog)s check-fgdata directory1 directory2...

  Check the specified directories assuming they contain some version of
  FGData. The 'check-fgdata' subcommand is similar to 'check-dirs' but has
  special handling for the 'Aircraft' top-level subdirectory of each of
  its arguments. This is because some of the subdirectories of the
  'Aircraft' top-level directory in FGData may be aircraft directories.

  $ %(prog)s fgdata-precommit-hook file1 file2...

  Must be run from the root directory of FGData. Check all PropertyList
  files among the arguments. Each such argument must be a path to an XML
  file. This path must be relative to the root of FGData and mustn't start
  with '.' or '..' (in other words, the first component must be a file
  or directory name different from '.' and '..').

  The 'fgdata-precommit-hook' subcommand automatically prepends the
  current directory to the list of include paths (no need to use
  --prepend-curdir-to-includes). Like the 'check-fgdata' subcommand, it
  has special handling for subdirectories of the 'Aircraft' top-level
  directory.

Exit status: zero if no problem was found, non-zero otherwise.""",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        # I want --help but not -h (it might be useful for something else)
        add_help=False)

    parser.add_argument("-I", "--include", action="append", default=[],
                        help="""\
      add an include directory, used to find the target files of PropertyList
      'include' directives (passing this option to specify a path to FGData
      is useful when checking an aircraft or add-on)""")
    parser.add_argument("--prepend-curdir-to-includes", action="store_true",
                        help="""\
      prepend the current directory to the list of include paths""")
    parser.add_argument("--help", action="help",
                        help="""\
      display this message and exit; “%(prog)s SUBCOMMAND --help” shows
      help for the specified subcommand.""")

    verbosityGroup = parser.add_mutually_exclusive_group()
    verbosityGroup.add_argument("-q", "--quiet", action="store_true",
                                help="""\
      don't write information when files are checked (you still have the
      exit status, however it obviously concerns all files as a whole)""")
    verbosityGroup.add_argument("-v", "--verbose", action="store_true",
                                help="""enable verbose output""")

    subparsers = parser.add_subparsers(title='subcommand', required=True)
    parser_checkXMLFiles = subparsers.add_parser("check-files", help="""\
      check each argument assuming it is an XML file""")
    parser_checkXMLFiles.set_defaults(func=checkXMLFilesFunction)
    parser_checkXMLFiles.add_argument("files", metavar="FILE", nargs="+",
                                      help="""XML file to check""")

    parser_recursiveDirCheck = subparsers.add_parser("check-dirs", help="""\
      check all XML files found under the specified directories (recursive
      exploration)""")
    parser_recursiveDirCheck.set_defaults(func=recursiveDirCheckFunction)
    parser_recursiveDirCheck.add_argument("directories", metavar="DIRECTORY",
                                          nargs="+",
                                          help="""directory to explore""")

    parser_specialFGDataCheck = subparsers.add_parser("check-fgdata", help="""\
      special check routine for FGData (not for use via pre-commit)""")
    parser_specialFGDataCheck.set_defaults(func=specialFGDataCheckFunction)
    parser_specialFGDataCheck.add_argument("directories", metavar="DIRECTORY",
                                          nargs="+",
                                          help="""directory to explore""")

    parser_FGDataPreCommitHook = subparsers.add_parser(
        "fgdata-precommit-hook", help="pre-commit hook for FGData")
    parser_FGDataPreCommitHook.set_defaults(func=fgdataPrecommitHookFunction)
    parser_FGDataPreCommitHook.add_argument("files", metavar="FILE", nargs="+",
                                            help="""XML file to check""")

    params = parser.parse_args()

    if params.prepend_curdir_to_includes:
        params.include.insert(0, os.getcwd())

    return params


def main():
    locale.setlocale(locale.LC_ALL, '')
    params = processCommandLine()

    return params.func(params)


if __name__ == "__main__":
    sys.exit(main())
