"""Cross-platform file modification check.

This is because the stat(1) utility has incompatible cross-platform usage.

Usage: lib-stat.py [ OPTIONS ] FILE ...

Options:

  -c TS, --check=TS    Assert that the given timestamp TS matches the given
                       FILE's modtime
  -d TS, --differs=TS  Assert that the given timestamp TS differs from the
                       given FILE's modtime

If no option is given print the modification time for each FILE to stdout.

"""

from __future__ import absolute_import, print_function


import datetime
import getopt
import os
import sys


class FloatTimesInStat(object):
    """Context manager to ensure that stat returns float values.

    For 3.7 <= Mercurial < 4.6: Mercurial calls :func:`os.stat_float_times`
    for `stat` and friends to return :class:`int` values.

    Temporarily fix this.

    """

    __slots__ = ("_do_reset",)

    def __init__(self):
        self._do_reset = False

    def __enter__(self):
        if hasattr(os, "stat_float_times"):
            if not os.stat_float_times():
                os.stat_float_times(True)
                self._do_reset = True
        return self

    def __exit__(self, *args):
        if self._do_reset:
            os.stat_float_times(False)


def to_isoformat(t):
    """Return the POSIX timestamp `t` formatted in full ISO format.

    `t` is expected to be in the UTC timezone.

    """
    dt = datetime.datetime.utcfromtimestamp(t)
    if dt.utcoffset() is None:
        return dt.isoformat() + "Z"
    else:
        return dt.isoformat()


def main():
    opt_check = None
    opt_differs = None

    opts, args = getopt.getopt(sys.argv[1:],
                               "c:d:",
                               ["check=",
                                "differs="])
    for opt, val in opts:
        if opt in ("-c", "--check"):
            opt_check = val
        elif opt in ("-d", "--differs"):
            opt_differs = val
        else:
            assert False

    if opt_check:
        with FloatTimesInStat():
            for f in args:
                st = os.lstat(f)
                tss = to_isoformat(st.st_mtime)
                if tss != opt_check:
                    print(
                        "mtime does not match for file `{filename}': expected {expected}, current: {current}".format(
                            filename=f,
                            expected=opt_check,
                            current=tss),
                        file=sys.stderr)
                    return 1
    elif opt_differs:
        with FloatTimesInStat():
            for f in args:
                st = os.lstat(f)
                tss = to_isoformat(st.st_mtime)
                if tss == opt_differs:
                    print(
                        "mtime does not differ for file `{filename}': {timestamp}".format(
                            filename=f,
                            timestamp=tss),
                        file=sys.stderr)
                    return 1
    else:
        with FloatTimesInStat():
            for f in args:
                st = os.lstat(f)
                print(to_isoformat(st.st_mtime))
    return 0


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