#!/usr/bin/python
# Copyright 2019 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_mtl_internal_shaders.py:
#   Code generation for Metal backend's default shaders.
#   NOTE: don't run this script directly. Run scripts/run_code_generation.py.

import json
import os
import subprocess
import sys

sys.path.append('../..')
import angle_format
import gen_angle_format_table

template_header_boilerplate = """// GENERATED FILE - DO NOT EDIT.
// Generated by {script_name}
//
// Copyright 2020 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.
//
"""

def gen_shader_enums_code(angle_formats):

    code = """// This file is similar to src/libANGLE/renderer/FormatID_autogen.h but is used by Metal default
// shaders instead of C++ code.
//
"""

    code += "namespace rx\n"
    code += "{\n"
    code += "namespace mtl_shader\n"
    code += "{\n"
    code += "\n"
    code += "namespace FormatID\n"
    code += "{\n"
    code += "enum\n"
    code += "{\n"
    code += gen_angle_format_table.gen_enum_string(angle_formats) + '\n'
    code += "};\n\n"
    code += "}\n"
    code += "\n"
    code += "}\n"
    code += "}\n"

    return code


def find_clang():
    if os.name == 'nt':
        binary = 'clang-cl.exe'
    else:
        binary = 'clang++'

    clang = os.path.join('..', '..', '..', '..', '..', 'third_party', 'llvm-build',
                         'Release+Asserts', 'bin', binary)

    if not os.path.isfile(clang):
        raise Exception('Cannot find clang')

    return clang


def main():
    angle_format_script_files = [
        '../../angle_format_map.json', '../../angle_format.py', '../../gen_angle_format_table.py'
    ]
    src_files = [
        'blit.metal', 'clear.metal', 'gen_indices.metal', 'gen_mipmap.metal', 'copy_buffer.metal',
        'visibility.metal'
    ]

    # auto_script parameters.
    if len(sys.argv) > 1:
        inputs = angle_format_script_files + src_files + ['common.h', 'constants.h']
        outputs = ['format_autogen.h', 'mtl_default_shaders_src_autogen.inc']

        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

    os.chdir(sys.path[0])

    boilerplate_code = template_header_boilerplate.format(script_name=sys.argv[0])

    # -------- Generate shader constants -----------
    angle_to_gl = angle_format.load_inverse_table('../../angle_format_map.json')
    shader_formats_autogen = gen_shader_enums_code(angle_to_gl.keys())
    shader_autogen_header = boilerplate_code + shader_formats_autogen

    with open('format_autogen.h', 'wt') as out_file:
        out_file.write(shader_autogen_header)
        out_file.close()

    # -------- Combine and create shader source string -----------
    # Generate combined source
    clang = find_clang()

    # Use clang to preprocess the combination source. "@@" token is used to prevent clang from
    # expanding the preprocessor directive
    temp_fname = 'temp_master_source.metal'
    with open(temp_fname, 'wb') as temp_file:
        for src_file in src_files:
            temp_file.write('#include "%s"\n' % src_file)

    args = [clang]
    if not os.name == 'nt':
        args += ['-xc++']
    args += ['-E', temp_fname]

    combined_source = subprocess.check_output(args)

    # Remove '@@' tokens
    final_combined_src_string = combined_source.replace('@@', '')

    # Generate final file:
    with open('mtl_default_shaders_src_autogen.inc', 'wt') as out_file:
        out_file.write(boilerplate_code)
        out_file.write('\n')
        out_file.write('// C++ string version of combined Metal default shaders.\n\n')
        out_file.write('\n\nstatic char gDefaultMetallibSrc[] = R"(\n')
        out_file.write(final_combined_src_string)
        out_file.write('\n')
        out_file.write(')";\n')
        out_file.close()

    os.remove(temp_fname)


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