#!/usr/bin/env python3
# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This script generates a C header file with build flags.
#
# It takes a response file as input, which contains the build flags.
# The response file format is:
#   --flags <list of one or more flag values>
#
# The generated header file defines macros for each build flag, which can be
# used in C code to conditionally compile code based on the build flags.
#
# See the gen_buildflags target in /gn/BUILD.gn for usage.

import argparse
import os
import shlex
import sys
import re
from dataclasses import dataclass

COPYRIGHT_HEADER = '''/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'''


@dataclass
class Flag:
  name: str
  raw_value: str


class HeaderGenerator:
  """Generates the build flag header file."""

  def __init__(self, out_path: str, flags: list[Flag]):
    self._out_path = out_path
    self._flags = flags
    self._guard = re.sub(r'[^A-Z0-9_]', '_', out_path.upper()) + '_'

  def _get_define_value(self, value: str) -> str:
    if value == 'true':
      return '1'
    if value == 'false':
      return '0'
    return value

  def _escape_value(self, value: str) -> str:
    return value.replace('\\', '\\\\').replace('"', '\\"')

  def _generate_defines(self) -> str:
    defines = []
    for flag in self._flags:
      define = (f'#define PERFETTO_BUILDFLAG_DEFINE_{flag.name}() '
                f'({self._get_define_value(flag.raw_value)})')
      defines.append(define)
    return '\n'.join(defines)

  def generate(self) -> None:
    boolean_flags = [f for f in self._flags if f.raw_value in ('true', 'false')]
    num_boolean_flags = len(boolean_flags)

    items = []
    for flag in boolean_flags:
      items.append(
          f'    {{"{flag.name}", PERFETTO_BUILDFLAG_DEFINE_{flag.name}()}},')
    all_build_flags_str = '\n'.join(items)

    header_template = f"""// Generated by {os.path.basename(__file__)}

// fix_include_guards: off
#ifndef {self._guard}
#define {self._guard}

// clang-format off

{self._generate_defines()}

struct PerfettoBuildFlag {{
  const char* name;
  int value;
}};

static const struct PerfettoBuildFlag kPerfettoBuildFlags[] = {{
{all_build_flags_str}
}};

static const int kPerfettoBuildFlagsCount = sizeof(kPerfettoBuildFlags) / sizeof(struct PerfettoBuildFlag);

// clang-format on

#endif  // {self._guard}
"""

    with open(self._out_path, 'w', newline='\n') as out:
      out.write(COPYRIGHT_HEADER)
      out.write(header_template)


def parse_flags_from_rsp(rsp_file: str) -> list[Flag]:
  """Parses the response file and returns a list of flags."""
  with open(rsp_file, 'r') as f:
    content = f.read()

  parts = shlex.split(content)
  if not parts or parts[0] != '--flags':
    raise ValueError("Invalid response file format: --flags not found.")

  flags: list[Flag] = []
  for flag_str in parts[1:]:
    if '=' not in flag_str:
      raise ValueError(f"Invalid flag format: {flag_str}")
    key, raw_value = flag_str.split('=', 1)
    flags.append(Flag(name=key, raw_value=raw_value))
  return flags


def main():
  parser = argparse.ArgumentParser()
  parser.add_argument('--rsp', help='Input response file containing the flags.')
  parser.add_argument(
      '--out', required=True, help='Output path of the generated header file.')
  args = parser.parse_args()

  flags = parse_flags_from_rsp(args.rsp)
  HeaderGenerator(args.out, flags).generate()


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