#!/usr/bin/python
# Copyright 2017 The ANGLE Project Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# gen_emulated_builtin_function_tables.py:
#  Generator for the builtin function maps.
#  NOTE: don't run this script directly. Run scripts/run_code_generation.py.

from datetime import date
import json
import os, sys

template_emulated_builtin_functions_hlsl = """// GENERATED FILE - DO NOT EDIT.
// Generated by {script_name} using data from {data_source_name}.
//
// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// emulated_builtin_functions_hlsl:
//   HLSL code for emulating GLSL builtin functions not present in HLSL.

#include "compiler/translator/BuiltInFunctionEmulator.h"
#include "compiler/translator/tree_util/BuiltIn_autogen.h"

namespace sh
{{

namespace
{{

struct FunctionPair
{{
   constexpr FunctionPair(const TSymbolUniqueId &idIn, const char *bodyIn) : id(idIn.get()), body(bodyIn)
   {{
   }}

   int id;
   const char *body;
}};

constexpr FunctionPair g_hlslFunctions[] = {{
{emulated_functions}}};
}}  // anonymous namespace

const char *FindHLSLFunction(int uniqueId)
{{
    for (size_t index = 0; index < ArraySize(g_hlslFunctions); ++index)
    {{
        const auto &function = g_hlslFunctions[index];
        if (function.id == uniqueId)
        {{
            return function.body;
        }}
    }}

    return nullptr;
}}
}}  // namespace sh
"""


def reject_duplicate_keys(pairs):
    found_keys = {}
    for key, value in pairs:
        if key in found_keys:
            raise ValueError("duplicate key: %r" % (key,))
        else:
            found_keys[key] = value
    return found_keys


def load_json(path):
    with open(path) as map_file:
        file_data = map_file.read()
        map_file.close()
        return json.loads(file_data, object_pairs_hook=reject_duplicate_keys)


def enum_type(arg):
    # handle 'argtype argname' and 'out argtype argname'
    chunks = arg.split(' ')
    arg_type = chunks[0]
    if len(chunks) == 3:
        arg_type = chunks[1]

    suffix = ""
    if not arg_type[-1].isdigit():
        suffix = '1'
    if arg_type[0:4] == 'uint':
        return 'UI' + arg_type[2:] + suffix
    return arg_type.capitalize() + suffix


def gen_emulated_function(data):

    func = ""
    if 'comment' in data:
        func += "".join(["// " + line + "\n" for line in data['comment']])

    sig = data['return_type'] + ' ' + data['op'] + '_emu(' + ', '.join(data['args']) + ')'
    body = [sig, '{'] + ['    ' + line for line in data['body']] + ['}']

    func += "{\n"
    func += "BuiltInId::" + data['op'] + "_" + "_".join([enum_type(arg) for arg in data['args']
                                                        ]) + ",\n"
    if 'helper' in data:
        func += '"' + '\\n"\n"'.join(data['helper']) + '\\n"\n'
    func += '"' + '\\n"\n"'.join(body) + '\\n"\n'
    func += "},\n"
    return [func]


def main():

    input_script = "emulated_builtin_function_data_hlsl.json"
    hlsl_fname = "emulated_builtin_functions_hlsl_autogen.cpp"

    # auto_script parameters.
    if len(sys.argv) > 1:
        inputs = [input_script]
        outputs = [hlsl_fname]

        if sys.argv[1] == 'inputs':
            print ','.join(inputs)
        elif sys.argv[1] == 'outputs':
            print ','.join(outputs)
        else:
            print('Invalid script parameters')
            return 1
        return 0

    hlsl_json = load_json(input_script)
    emulated_functions = []

    for item in hlsl_json:
        emulated_functions += gen_emulated_function(item)

    hlsl_gen = template_emulated_builtin_functions_hlsl.format(
        script_name=sys.argv[0],
        data_source_name=input_script,
        copyright_year=date.today().year,
        emulated_functions="".join(emulated_functions))

    with open(hlsl_fname, 'wt') as f:
        f.write(hlsl_gen)
        f.close()

    return 0


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