#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

# fix_trailing_whitespace.py script ensures that all files passed into it satisfy
# various requirements in terms of whitespace:
# - There is no leading whitespace in the file.
# - Lines do not have trailing non-newline whitespace and have UNIX-style line endings.
# - The file ends in a single newline.

import argparse
import sys
import subprocess

from pathlib import Path

# This file is $REPO_TOP/util/fix_trailing_newlines.py, so it takes two parent()
# calls to get back to the top.
REPO_TOP = Path(__file__).resolve().parent.parent

IGNORED = {
    Path('sw/device/silicon_creator/rom/e2e/presigned_images/rom_e2e_self_hash_sim_dv.logs.txt'),
    Path('sw/device/silicon_creator/rom/e2e/presigned_images/rom_e2e_self_hash_sim_dv.rodata.txt'),
}


def is_ignored(path):
    if path in IGNORED:
        return True
    return subprocess.run(['git', 'check-ignore', path]).returncode == 0


def walk_tree(paths=[REPO_TOP]):
    for path in paths:
        if isinstance(path, str):
            path = Path(path)

        if path.is_symlink() or is_ignored(path) or 'LICENSE' in path.parts:
            continue

        if path.is_dir() and 'vendor' not in path.parts:
            yield from walk_tree(path.iterdir())
        else:
            yield path


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--dry-run',
        action='store_true',
        help='report writes which would have happened')
    parser.add_argument(
        '--recursive', '-r',
        action='store_true',
        default=False,
        help='traverse the entire tree modolo .gitignore'
    )
    parser.add_argument(
        '--verbose', '-v',
        action='store_true',
        help='verbose output')
    parser.add_argument(
        'files',
        type=str,
        nargs='*',
        help='files to fix whitespace for')
    args = parser.parse_args()

    files = args.files
    if args.recursive:
        files = walk_tree(args.files)

    total_fixable = 0
    for path in files:
        path = Path(path).resolve().relative_to(REPO_TOP)
        if not path.is_file() or path.is_symlink() or is_ignored(path):
            continue
        if 'vendor' in path.parts or path.suffix in ['.patch', '.svg', '.tpl']:
            continue
        if args.verbose:
            print(f'Checking: "{path}"')

        try:
            old_text = path.read_text()
        except UnicodeDecodeError:
            print(f'Binary file: "{path}"')
            continue
        new_text = "\n".join([line.rstrip() for line in old_text.strip().split("\n")]) + "\n"

        if old_text != new_text:
            print(f'Fixing file: "{path}"', file=sys.stdout)
            total_fixable += 1
            if not args.dry_run:
                path.write_text(new_text)

    if total_fixable:
        verb = 'Would have fixed' if args.dry_run else 'Fixed'
        print(f'{verb} {total_fixable} files.', file=sys.stderr)

    # Pass if we fixed everything or there was nothing to fix.
    return 1 if total_fixable > 0 else 0


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