# SPDX-License-Identifier: GPL-2.0-or-later

"""
Utility for reporting deprecated code which should be removed,
noted by the date which must be included with the *DEPRECATED* comment.

Once this date is past, the code should be removed.
"""

from typing import (
    Callable,
    Generator,
    List,
    Tuple,
    Optional,
)


import os
import datetime
from os.path import splitext

SKIP_DIRS = (
    "extern",
    # Not this directory.
    "tests",
)


class term_colors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKCYAN = '\033[96m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'


def is_c_header(filename: str) -> bool:
    ext = splitext(filename)[1]
    return (ext in {".h", ".hh", ".hpp", ".hxx", ".hh"})


def is_c(filename: str) -> bool:
    ext = splitext(filename)[1]
    return (ext in {".c", ".cc", ".cpp", ".cxx", ".m", ".mm", ".rc", ".inl"})


def is_c_any(filename: str) -> bool:
    return is_c(filename) or is_c_header(filename)


def is_py(filename: str) -> bool:
    ext = splitext(filename)[1]
    return (ext == ".py")


def is_source_any(filename: str) -> bool:
    return is_c_any(filename) or is_py(filename)


def source_list(path: str, filename_check: Optional[Callable[[str], bool]] = None) -> Generator[str, None, None]:
    for dirpath, dirnames, filenames in os.walk(path):
        # skip '.git'
        dirnames[:] = [d for d in dirnames if not d.startswith(".")]

        for filename in filenames:
            if filename_check is None or filename_check(filename):
                yield os.path.join(dirpath, filename)


def deprecations() -> List[Tuple[datetime.datetime, Tuple[str, int], str]]:
    """
    Searches out source code for lines like

    /* *DEPRECATED* 2011/7/17 bgl.Buffer.list info text. */

    Or...

    # *DEPRECATED* 2010/12/22 some.py.func more info.

    """
    SOURCE_DIR = os.path.normpath(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")))

    SKIP_DIRS_ABS = [os.path.join(SOURCE_DIR, p) for p in SKIP_DIRS]

    DEPRECATED_ID = "*DEPRECATED*"
    depprecation_list = []

    scan_count = 0

    print("Scanning in %r for '%s YYYY/MM/DD info'" % (SOURCE_DIR, DEPRECATED_ID), end="...")

    for fn in source_list(SOURCE_DIR, is_source_any):
        if os.path.samefile(fn, __file__):
            continue

        skip = False
        for p in SKIP_DIRS_ABS:
            if fn.startswith(p):
                skip = True
                break
        if skip:
            continue

        with open(fn, 'r', encoding="utf8") as fh:
            fn = os.path.relpath(fn, SOURCE_DIR)
            buf = fh.read()
            index = 0
            while True:
                index = buf.find(DEPRECATED_ID, index)
                if index == -1:
                    break
                index_end = buf.find("\n", index)
                if index_end == -1:
                    index_end = len(buf)
                line_number = buf[:index].count("\n") + 1
                l = buf[index + len(DEPRECATED_ID): index_end].strip()
                try:
                    data = [w.strip() for w in l.split('/', 2)]
                    data[-1], info = data[-1].split(' ', 1)
                    info = info.split("*/", 1)[0].strip()
                    if len(data) != 3:
                        print(
                            "    poorly formatting line:\n"
                            "    %r:%d\n"
                            "    %s" %
                            (fn, line_number, data)
                        )
                    else:
                        depprecation_list.append((
                            datetime.datetime(int(data[0]), int(data[1]), int(data[2])),
                            (fn, line_number),
                            info,
                        ))
                except:
                    print("Error file - %r:%d" % (fn, line_number))
                    import traceback
                    traceback.print_exc()

                index = index_end

        scan_count += 1

    print(" {:d} files done, found {:d} deprecation(s)!".format(scan_count, len(depprecation_list)))

    return depprecation_list


def main() -> None:
    import datetime
    now = datetime.datetime.now()

    deps = deprecations()

    for data, fileinfo, info in deps:
        days_old = (now - data).days
        info = term_colors.BOLD + info + term_colors.ENDC
        if days_old > 0:
            info = "[" + term_colors.FAIL + "REMOVE" + term_colors.ENDC + "] " + info
        else:
            info = "[" + term_colors.OKBLUE + "OK" + term_colors.ENDC + "] " + info

        print("{:s}: days-old({:d}), {:s}:{:d} {:s}".format(
            data.strftime("%Y/%m/%d"),
            days_old,
            fileinfo[0],
            fileinfo[1],
            info,
        ))


if __name__ == '__main__':
    main()