#!/usr/bin/env python3
#
# Copyright 2016 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Tool that combines a sequence of input proguard files and outputs a single
# proguard file.
#
# The final output file is formed by concatenating all of the
# input proguard files.

import argparse
import pathlib
import os
import re
import sys
import json

REPOSITORY_ROOT = os.path.abspath(
    os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir))

sys.path.insert(0, os.path.join(REPOSITORY_ROOT, 'build'))
import action_helpers  # pylint: disable=wrong-import-position


def _ReadFile(path):
    """Reads a file as a string."""
    return pathlib.Path(path).read_text()


def _post_process_concatenated_rules(rules: str, rename_map: dict) -> str:
    """Post-process the concatenated rules to rename packages.

    Args:
      rules: Rules before processing
      rename_map: Dict mapping source package to destination package
    """
    for src, dest in rename_map.items():
        # This regex will match anything substring that matches the source package
        # name but is not preceded by either '*' or '/'.
        rules = re.sub(rf"([^*/]){re.escape(src)}", rf"\g<1>{dest}", rules)
    return rules


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--output_file',
                        help='Output file for the generated proguard file')
    parser.add_argument(
        '--rename-rule',
        action='append',
        default=[],
        help='Renaming rules in the format "source_package=dest_package". '
        'Can be repeated.')
    parser.add_argument('--dep_file',
                        help='Depfile path to write the implicit inputs')
    parser.add_argument(
        'build_config',
        help='Path to the generated build_config that contains the transitive '
        'dependencies of the proguard rules')

    args = parser.parse_args()

    rename_map = {}
    for rule in args.rename_rule:
        if '=' not in rule:
            parser.error(
                f'Invalid rename rule: {rule}. Must be in "source=dest" format.'
            )
        src, dest = rule.split('=', 1)
        rename_map[src] = dest

    # Fetch all proguard configs
    with open(args.build_config, 'r') as f:
        build_config = json.load(f)
        all_proguard_configs_path = set(build_config['proguard_all_configs'])

    str_output = ""
    # Concatenate all proguard rules and sort to maintain deterministic output.
    for proguard_config_path in sorted(all_proguard_configs_path):
        noramlized_path = proguard_config_path.replace('../', '')
        str_output += f"# -------- Config Path: {noramlized_path} --------\n"
        str_output += _ReadFile(proguard_config_path)
    if rename_map:
        str_output = _post_process_concatenated_rules(str_output, rename_map)
    with open(args.output_file, 'w') as target:
        target.write(str_output)
    action_helpers.write_depfile(args.dep_file, args.output_file,
                                 all_proguard_configs_path)


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