#!/usr/bin/python2
#
# Copyright 2018 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.
#
# generate_loader.py:
#   Generates dynamic loaders for various binding interfaces.
#   NOTE: don't run this script directly. Run scripts/run_code_generation.py.

import sys, os, pprint, json
from datetime import date
import registry_xml


def write_header(data_source_name,
                 all_cmds,
                 api,
                 preamble,
                 path,
                 lib,
                 ns="",
                 prefix=None,
                 export=""):
    file_name = "%s_loader_autogen.h" % api
    header_path = registry_xml.path_to(path, file_name)

    def pre(cmd):
        if prefix == None:
            return cmd
        return prefix + cmd[len(api):]

    with open(header_path, "w") as out:
        var_protos = [
            "%sextern PFN%sPROC %s%s;" % (export, cmd.upper(), ns, pre(cmd)) for cmd in all_cmds
        ]
        loader_header = template_loader_h.format(
            script_name=os.path.basename(sys.argv[0]),
            data_source_name=data_source_name,
            year=date.today().year,
            function_pointers="\n".join(var_protos),
            api_upper=api.upper(),
            api_lower=api,
            preamble=preamble,
            export=export,
            lib=lib.upper(),
            load_fn_name="Load%s%s" % (prefix if prefix else "", api.upper()))

        out.write(loader_header)
        out.close()


def write_source(data_source_name, all_cmds, api, path, ns="", prefix=None, export=""):
    file_name = "%s_loader_autogen.cpp" % api
    source_path = registry_xml.path_to(path, file_name)

    def pre(cmd):
        if prefix == None:
            return cmd
        return prefix + cmd[len(api):]

    with open(source_path, "w") as out:
        var_defs = ["%sPFN%sPROC %s%s;" % (export, cmd.upper(), ns, pre(cmd)) for cmd in all_cmds]

        setter = "    %s%s = reinterpret_cast<PFN%sPROC>(loadProc(\"%s\"));"
        setters = [setter % (ns, pre(cmd), cmd.upper(), pre(cmd)) for cmd in all_cmds]

        loader_source = template_loader_cpp.format(
            script_name=os.path.basename(sys.argv[0]),
            data_source_name=data_source_name,
            year=date.today().year,
            function_pointers="\n".join(var_defs),
            set_pointers="\n".join(setters),
            api_upper=api.upper(),
            api_lower=api,
            load_fn_name="Load%s%s" % (prefix if prefix else "", api.upper()))

        out.write(loader_source)
        out.close()


def gen_libegl_loader():

    data_source_name = "egl.xml and egl_angle_ext.xml"
    xml = registry_xml.RegistryXML("egl.xml", "egl_angle_ext.xml")

    for major_version, minor_version in [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5]]:
        annotation = "{}_{}".format(major_version, minor_version)
        name_prefix = "EGL_VERSION_"

        feature_name = "{}{}".format(name_prefix, annotation)

        xml.AddCommands(feature_name, annotation)

    xml.AddExtensionCommands(registry_xml.supported_egl_extensions, ['egl'])

    all_cmds = xml.all_cmd_names.get_all_commands()

    path = os.path.join("..", "src", "libEGL")
    write_header(data_source_name, all_cmds, "egl", libegl_preamble, path, "LIBEGL", "", "EGL_")
    write_source(data_source_name, all_cmds, "egl", path, "", "EGL_")


def gen_gl_loader():

    data_source_name = "gl.xml and gl_angle_ext.xml"
    xml = registry_xml.RegistryXML("gl.xml", "gl_angle_ext.xml")

    # First run through the main GLES entry points.  Since ES2+ is the primary use
    # case, we go through those first and then add ES1-only APIs at the end.
    for major_version, minor_version in [[2, 0], [3, 0], [3, 1], [1, 0]]:
        annotation = "{}_{}".format(major_version, minor_version)
        name_prefix = "GL_ES_VERSION_"

        is_gles1 = major_version == 1
        if is_gles1:
            name_prefix = "GL_VERSION_ES_CM_"

        feature_name = "{}{}".format(name_prefix, annotation)

        xml.AddCommands(feature_name, annotation)

    xml.AddExtensionCommands(registry_xml.supported_extensions, ['gles2', 'gles1'])

    all_cmds = xml.all_cmd_names.get_all_commands()

    if registry_xml.support_EGL_ANGLE_explicit_context:
        all_cmds += [cmd + "ContextANGLE" for cmd in xml.all_cmd_names.get_all_commands()]

    path = os.path.join("..", "util")
    ex = "ANGLE_UTIL_EXPORT "
    write_header(data_source_name, all_cmds, "gles", util_gles_preamble, path, "UTIL", export=ex)
    write_source(data_source_name, all_cmds, "gles", path, export=ex)


def gen_egl_loader():

    data_source_name = "egl.xml and egl_angle_ext.xml"
    xml = registry_xml.RegistryXML("egl.xml", "egl_angle_ext.xml")

    for major_version, minor_version in [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5]]:
        annotation = "{}_{}".format(major_version, minor_version)
        name_prefix = "EGL_VERSION_"

        feature_name = "{}{}".format(name_prefix, annotation)

        xml.AddCommands(feature_name, annotation)

    xml.AddExtensionCommands(registry_xml.supported_egl_extensions, ['egl'])

    all_cmds = xml.all_cmd_names.get_all_commands()

    path = os.path.join("..", "util")
    ex = "ANGLE_UTIL_EXPORT "
    write_header(data_source_name, all_cmds, "egl", util_egl_preamble, path, "UTIL", export=ex)
    write_source(data_source_name, all_cmds, "egl", path, export=ex)


def gen_wgl_loader():

    supported_wgl_extensions = [
        "WGL_ARB_create_context",
        "WGL_ARB_extensions_string",
        "WGL_EXT_swap_control",
    ]

    source = "wgl.xml"
    xml = registry_xml.RegistryXML(source)

    for major_version, minor_version in [[1, 0]]:
        annotation = "{}_{}".format(major_version, minor_version)
        name_prefix = "WGL_VERSION_"

        feature_name = "{}{}".format(name_prefix, annotation)

        xml.AddCommands(feature_name, annotation)

    xml.AddExtensionCommands(supported_wgl_extensions, ['wgl'])

    all_cmds = xml.all_cmd_names.get_all_commands()

    path = os.path.join("..", "util", "windows")
    write_header(source, all_cmds, "wgl", util_wgl_preamble, path, "UTIL_WINDOWS", "_")
    write_source(source, all_cmds, "wgl", path, "_")


def main():

    # Handle inputs/outputs for run_code_generation.py's auto_script
    if len(sys.argv) > 1:
        inputs = [
            'egl.xml',
            'egl_angle_ext.xml',
            'registry_xml.py',
            'wgl.xml',
        ]
        outputs = [
            '../src/libEGL/egl_loader_autogen.cpp',
            '../src/libEGL/egl_loader_autogen.h',
            '../util/egl_loader_autogen.cpp',
            '../util/egl_loader_autogen.h',
            '../util/gles_loader_autogen.cpp',
            '../util/gles_loader_autogen.h',
            '../util/windows/wgl_loader_autogen.cpp',
            '../util/windows/wgl_loader_autogen.h',
        ]

        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

    gen_libegl_loader()
    gen_gl_loader()
    gen_egl_loader()
    gen_wgl_loader()
    return 0


libegl_preamble = """#include <EGL/egl.h>
#include <EGL/eglext.h>
"""

util_gles_preamble = """#if defined(GL_GLES_PROTOTYPES) && GL_GLES_PROTOTYPES
#error "Don't define GL prototypes if you want to use a loader!"
#endif  // defined(GL_GLES_PROTOTYPES)

#include "angle_gl.h"
#include "util/util_export.h"
"""

util_egl_preamble = """#include "util/util_export.h"

#include <EGL/egl.h>
#include <EGL/eglext.h>
"""

util_wgl_preamble = """
#include <WGL/wgl.h>
#include <GLES2/gl2.h>

// We add an underscore before each function name to ensure common names like "ChoosePixelFormat"
// and "SwapBuffers" don't conflict with our function pointers. We can't use a namespace because
// some functions conflict with preprocessor definitions.
"""

template_loader_h = """// GENERATED FILE - DO NOT EDIT.
// Generated by {script_name} using data from {data_source_name}.
//
// 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.
//
// {api_lower}_loader_autogen.h:
//   Simple {api_upper} function loader.

#ifndef {lib}_{api_upper}_LOADER_AUTOGEN_H_
#define {lib}_{api_upper}_LOADER_AUTOGEN_H_

{preamble}
{function_pointers}

namespace angle
{{
using GenericProc = void (*)();
using LoadProc = GenericProc (KHRONOS_APIENTRY *)(const char *);
{export}void {load_fn_name}(LoadProc loadProc);
}}  // namespace angle

#endif  // {lib}_{api_upper}_LOADER_AUTOGEN_H_
"""

template_loader_cpp = """// GENERATED FILE - DO NOT EDIT.
// Generated by {script_name} using data from {data_source_name}.
//
// 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.
//
// {api_lower}_loader_autogen.cpp:
//   Simple {api_upper} function loader.

#include "{api_lower}_loader_autogen.h"

{function_pointers}

namespace angle
{{
void {load_fn_name}(LoadProc loadProc)
{{
{set_pointers}
}}
}}  // namespace angle
"""

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