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

import argparse
import ctypes
import os
import re
import subprocess
import sys
import typing

# Import the UKM codegen library for its hashing function, which is the same
# hashing function as used for flag names.
# TODO(crbug.com/1371214) Move `codegen.HashName()` somewhere common so we don't
#  depend on 'ukm'.
sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, 'ukm'))
import codegen

sys.path.append(
    os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir,
                 os.pardir, 'python', 'google'))
import path_utils

import pretty_print


def get_entries_from_unit_test(outdir: str) -> typing.List[str]:
  """Returns `<int>` entries reported missing by the 'CheckHistograms' unittest.
  """
  subprocess.run(['autoninja', '-C', outdir, 'unit_tests'])
  run_test_command = subprocess.run([
      os.path.join(outdir, 'unit_tests'),
      '--gtest_filter=AboutFlagsHistogramTest.CheckHistograms'
  ],
                                    capture_output=True,
                                    text=True)
  return re.findall('<int [^>]*>', run_test_command.stdout)


def get_entries_from_feature_string(feature: str) -> typing.List[str]:
  """Generates entries for `feature`."""
  entries = []
  for suffix in ['disabled', 'enabled']:
    label = f'{feature}:{suffix}'
    value_64 = codegen.HashName(label)
    value_32 = ctypes.c_int32(value_64).value
    entries.append(f'<int value="{value_32}" label="{label}"/>')
  return entries


def add_entries_to_xml(enums_xml: str, entries: typing.List[str]) -> str:
  """Adds each of `entries` to `enums_xml` and pretty prints it."""
  # Only add entries not already present.
  entries = [entry for entry in entries if enums_xml.find(entry) == -1]
  if entries:
    find_text = '<enum name="LoginCustomFlags">'
    find_index = enums_xml.find(find_text)
    if find_index == -1:
      raise Exception(f'Missing {find_text} in enums.xml.')
    find_index += len(find_text)
    enums_xml = (enums_xml[:find_index] + ' '.join(entries) +
                 enums_xml[find_index:])
  return pretty_print.PrettyPrintEnums(enums_xml)


def main():
  """Generates and formats flag enums.

  Args:
    outdir: (Optional) The build output directory, defaults to out/Default.
    feature: (Optional) The feature associated with the flag added. If omitted,
      will determine it by building and running `unit_tests
      AboutFlagsHistogramTest.CheckHistograms`. If provided, there's no use also
      providing `outdir`, as nothing needs to be built.
  Example usage:
    generate_flag_enums.py
    generate_flag_enums.py out/Default
    generate_flag_enums.py --feature MyFeatureString
  """

  parser = argparse.ArgumentParser()
  parser.add_argument(
      'outdir',
      nargs='?',
      default='out/Default',
      help='(Optional) The build output directory, defaults to out/Default.')
  parser.add_argument(
      '--feature',
      help="(Optional) The feature associated with the flag added. If omitted, "
      "will determine it by building and running `unit_tests "
      "AboutFlagsHistogramTest.CheckHistograms`. If provided, there's no use "
      "also providing `outdir`, as nothing needs to be built.")
  args = parser.parse_args()

  entries = get_entries_from_feature_string(args.feature) \
    if args.feature else get_entries_from_unit_test(args.outdir)

  if not entries:
    print("No missing enum entries found.")
    return

  xml_dir = path_utils.ScriptDir()
  xml_path = os.path.join(xml_dir, 'enums.xml')

  # Add any missing flag entries to enums.xml.
  with open(xml_path, 'r+') as fd:
    enums_xml = fd.read()
    enums_xml = add_entries_to_xml(enums_xml, entries)
    # Write back the entries to enums.xml.
    fd.seek(0)
    fd.write(enums_xml)

  # Print any changes.
  subprocess.run(['git', 'diff', xml_path])


if __name__ == '__main__':
  main()
