#!/usr/bin/env python

"""

Copyright (C) 2011, 2014 - 2016, 2019 - 2024
Smithsonian Astrophysical Observatory


This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program 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 for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Usage:

  ./check_ciao_version

  Use --help for further information.

Aim:

  Check that the CIAO and CALDB installations are up to date.

  If called with no argument then the tool will check against a
  version file on the CXC web site, and so requires on-line access.

  CIAO must have been started before running this tool, and the
  CIAO installation should have been made with either ciao-install
  or conda.

  If the system needs an update then the tool will exit with a
  non-zero status.

"""

import os
from pathlib import Path
import sys

from optparse import OptionParser

toolname = "check_ciao_version"
version = "26 November 2024"

try:
    from ciao_contrib import logger_wrapper as lw
    from ciao_contrib._tools import versioninfo
    from ciao_contrib.caldb import get_caldb_dir, \
        get_caldb_installed, check_caldb_version

except ImportError:
    sys.stderr.write(f"# {toolname} ({version}): ERROR " +
                     "Unable to load a CIAO module. Has CIAO " +
                     "been started?\n")
    sys.exit(1)


lgr = lw.initialize_logger(toolname)


help_str = """
Check that the installed CIAO is up to date.

The script checks that the installed CIAO and CALDB is up to date,
which requres that it downloads information from the CIAO web site.

"""


copyright_str = """
Copyright (C) 2011, 2014 - 2016, 2019 - 2024
Smithsonian Astrophysical Observatory

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program 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 for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""


def report_update_info_ciao_install():
    "Write out generic 'how to install CIAO' info"

    print("\nPlease use the ciao-install script from")
    print("    https://cxc.harvard.edu/ciao/download/ciao_install.html")
    print("to update your CIAO installation.")


def compare_versions(ciao, latest, installed):
    """Compare the versions, displaying all those packages that are
    out of date. ciao is the value of $ASCDS_INSTALL.

    We special case the over-all version; if this is out of date then
    we do not bother checking any sub-packages and we exit the program
    with a non-zero exit status.

    True is returned if all packages are up to date, False otherwise.

    """

    # We assume that the installed version can not be newer than the
    # latest version.
    #
    vlatest = latest["CIAO"]
    vinstalled = installed["CIAO"]

    # Automatically error out if this fails, as we do not
    # need to do the package check. We return False so
    # we can check the CALDB as that may be up to date.
    #
    if vlatest != vinstalled:
        print(f"The CIAO installation at {ciao}")
        print(f"   has version             {vinstalled}")
        print(f"   the released version is {vlatest}")
        print("")
        return False

    updates = []
    for (ik, iv) in installed.items():
        if ik == "CIAO":
            continue

        # It is unlikely that ik is not a member of latest,
        # so do not bother worrying about it.
        #
        lv = latest[ik]
        if iv != lv:
            updates.append((ik, iv, lv))

    if updates == []:
        print(f"The CIAO  installation at {ciao} is up to date.")
        return True

    print(f"Using the CIAO installation at {ciao}\n")

    nu = len(updates)
    if nu == 1:
        print("The following package:")
    else:
        print("The following packages:")

    for (pname, iver, _) in updates:
        print(f"    {pname:10s}:  {iver}")

    if nu == 1:
        print("\nneeds updating to:")
    else:
        print("\nneed updating to:")

    for (pname, _, lver) in updates:
        print(f"    {pname:10s}:  {lver}")

    print("")
    return False


# Logic also in check_ciao_caldb: should re-factor
#
def compare_caldb():
    """Check the CALDB is up to date."""

    try:
        caldb = get_caldb_dir()
    except OSError as oe:
        if str(oe) == "CALDB environment variable is not defined!":
            print("WARNING: CALDB environment variable is not defined.")
            return True
        else:
            raise oe

    inst = get_caldb_installed(caldb)
    cver = inst[0]
    rval = check_caldb_version(cver)

    cinfo = f"The CALDB installation at {caldb}"
    if os.path.islink(caldb):
        lname = os.path.realpath(caldb)
        cinfo += f" (link to {lname})"

    if rval is None:
        print(f"{cinfo} is up to date.")
        return True

    print(cinfo)
    print(f"  has version: {cver}")
    print(f"  latest is:   {rval[1]}")
    return False


@lw.handle_ciao_errors(toolname, version)
def doit():
    """Run the code."""

    opts = OptionParser(usage="%prog",
                        description=help_str)
    opts.add_option("-c", "--copyright", dest="list_copyright",
                    action="store_true",
                    help="List the copyright for the script and exit.")
    opts.add_option("-v", "--version", dest="list_version",
                    action="store_true",
                    help="List the version of the script and exit.")

    opts.add_option("--debug", dest="debug",
                    action="store_true",
                    help="Provide debugging information.")

    opts.set_defaults(list_copyright=False, list_version=False)

    (options, args) = opts.parse_args()

    if options.debug:
        lw.set_verbosity(5)
        lw.set_handle_ciao_errors_debug(True)

    nargs = len(args)
    if nargs > 1:
        opts.print_help()
        return

    if options.list_copyright:
        print(copyright_str)
        return

    if options.list_version:
        print(version)
        return

    ciao = os.getenv("ASCDS_INSTALL")
    if ciao is None:
        # unlikely to get here since able to load ciao_contrib
        raise OSError("$ASCDS_INSTALL is not defined; has CIAO been started?")

    ciao_path = Path(ciao)

    # How was CIAO installed?
    #  - ciao-install  has $ASCDS_INSTALL/binexe directory
    #  - conda         does not have ...
    #
    # [although how long this will work as a discriminator is unknown]
    #
    canary = ciao_path / "binexe"
    if not canary.is_dir():
        # We rely on conda for all the checks so can exit here.
        #
        flag = versioninfo.check_conda_versions(ciao)
        sys.exit(0 if flag else 1)

    installed = versioninfo.get_installed_versions(ciao)
    if installed is None:
        raise OSError("Unable to find any $ASCDS_INSTALL/VERSION* files")

    if nargs == 1:
        latest = versioninfo.read_latest_versions(args[0])
    else:
        latest = versioninfo.get_latest_versions()

    # Check versions. Note that CALDB not be set.
    #
    flag = compare_versions(ciao, latest, installed)
    flag &= compare_caldb()
    if not flag:
        report_update_info_ciao_install()
        sys.exit(1)


if __name__ == "__main__":
    doit()
