#!/usr/bin/env python3
import re
import sys

from argparse import ArgumentParser

# This is a very dumb regex based method of translating from
# gn variable declarations and simple if statements to the
# equivalent cmake code. It only supports a few constructs and
# assumes the code is formatted a certain way. ANGLE gn files
# are auto-formatted so this should work most of the time.

# The output may need a bit of manual fixup, but it beats
# translating the whole thing by hand. If new constructs
# are added to the ANGLE gn files, hopefully we can just add
# a few extra regexes here.

parser = ArgumentParser(
    prog='gni-to-cmake', description='converts a .gni file to .cmake', usage='%(prog)s [options]')
parser.add_argument('gni', help='the .gni file to parse')
parser.add_argument('cmake', help='the .cmake file to output')
parser.add_argument('--prepend', default='', help='the path to prepend to each file name')

args = parser.parse_args()

with open(args.gni, 'r', encoding="utf-8") as gni:
    file = gni.read()

    # Prepend path to every string.
    file = re.sub(r'"((\\\"|[^"])+)"', '"' + args.prepend + r'\1"', file)

    # Remove import, assert, config, and angle_source_set statements.
    file = re.sub(
        r'^\s*(import|assert|config|angle_source_set)\([^)]+\)( {.*})?$',
        r'',
        file,
        flags=re.MULTILINE | re.DOTALL)

    # Remove pkg_config and declare_args statements after removing import statements.
    # Import statements can refer to pkg_config, necessating handling this separately,
    # and the regexps are slightly different.
    file = re.sub(
        r'(^\s*(pkg_config|declare_args)\([^)]*\)\s+{.*?}$)*',
        r'',
        file,
        flags=re.MULTILINE | re.DOTALL)

    # Translate gn single line list declaration:
    file = re.sub(r'\[ ((?:"[^"]*",? )*)\]$', r' \1)', file, flags=re.MULTILINE)

    # Translate gn single line list append:
    file = re.sub(
        r'^(\s*)(\w+) \+= (\w+)$', r'\1 set(\2, "${\2};${\3}")', file, flags=re.MULTILINE)

    # Translate gn list declaration:
    # variable_name = [
    #   "file/name/foo.cpp",
    # ]
    # to cmake list declaration:
    # set(variable_name
    #     "file/name/foo.cpp"
    # )
    file = re.sub(r'^(\s*)(\w+) = ?\[?', r'\1\1set(\2', file, flags=re.MULTILINE)
    file = re.sub(r'^(\s*)("[^"]+"),$', r'\1\1\2', file, flags=re.MULTILINE)
    file = re.sub(r'^(\s*)]$', r'\1\1)', file, flags=re.MULTILINE)

    # Translate list append fom gn to cmake
    file = re.sub(r'^(\s*)(\w+) \+= ?\[?', r'\1\1list(APPEND \2', file, flags=re.MULTILINE)

    # Translate if statements from gn to cmake
    file = re.sub(r'^(\s*)((?:} else )?)if \((.+)\) {$', r'\1\1\2if(\3)', file, flags=re.MULTILINE)
    file = re.sub(r'^} else if$', r'elseif', file, flags=re.MULTILINE)
    file = re.sub(r'^(\s*)} else {$', r'\1\1else()', file, flags=re.MULTILINE)
    file = re.sub(r'^(\s*)}$', r'\1\1endif()', file, flags=re.MULTILINE)

    # Translate logic ops from gn to cmake
    file = re.sub(r' \|\| ', r' OR ', file, flags=re.MULTILINE)
    file = re.sub(r' \&\& ', r' AND ', file, flags=re.MULTILINE)
    file = re.sub(r' == ', r' STREQUAL ', file, flags=re.MULTILINE)
    file = re.sub(r'!', r' NOT ', file, flags=re.MULTILINE)

    with open(args.cmake, 'w', encoding="utf-8") as cmake:
        cmake.write('# This file was generated with the command:\n')
        cmake.write('# ' + ' '.join(['"' + arg.replace('"', '\\"') + '"' for arg in sys.argv]))
        cmake.write('\n\n')
        cmake.write(file)
