load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_binary", "closure_js_library")

def _internal_closure_fragment_export_impl(ctx):
    ctx.actions.write(
        output = ctx.outputs.out,
        content = """
goog.require('%s');
goog.exportSymbol('_', %s);
""" % (ctx.attr.module, ctx.attr.function),
    )

_internal_closure_fragment_export = rule(
    implementation = _internal_closure_fragment_export_impl,
    attrs = {
        "function": attr.string(mandatory = True),
        "module": attr.string(mandatory = True),
    },
    outputs = {"out": "%{name}.js"},
)

def _closure_fragment_impl(ctx):
    defs = ctx.attr.defines + [
        "goog.userAgent.ASSUME_MOBILE_WEBKIT=true",
        "goog.userAgent.product.ASSUME_ANDROID=true",
    ]

def closure_fragment(
        name,
        module,
        function,
        browsers = None,
        **kwargs):
    if browsers == None:
        browsers = []
    if type(browsers) != "list":
        fail("browsers must be None or a list of strings")

    exports_file_name = "_%s_exports" % name
    _internal_closure_fragment_export(
        name = exports_file_name,
        module = module,
        function = function,
    )

    exports_lib_name = "%s_lib" % exports_file_name
    closure_js_library(
        name = exports_lib_name,
        srcs = [exports_file_name],
        deps = kwargs.get("deps", []),
    )

    # Wrap the output in two functions. The outer function ensures the
    # compiled fragment never pollutes the global scope by using its
    # own scope on each invocation. We must import window.navigator into
    # this unique scope since Closure's goog.userAgent package assumes
    # navigator and document are defined on goog.global. Normally, this
    # would be window, but we are explicitly defining the fragment so that
    # goog.global is _not_ window.
    #     See http://code.google.com/p/selenium/issues/detail?id=1333
    wrapper = (
        "function(){" +
        "return (function(){%output%; return this._.apply(null,arguments);}).apply({" +
        "navigator:typeof window!='undefined'?window.navigator:null," +
        "document:typeof window!='undefined'?window.document:null" +
        "}, arguments);}"
    )

    browser_defs = {
        "*": [],
        "android": [
            "--define=goog.userAgent.ASSUME_MOBILE_WEBKIT=true",
            "--define=goog.userAgent.product.ASSUME_ANDROID=true",
        ],
        "chrome": [
            "--define=goog.userAgent.ASSUME_WEBKIT=true",
            "--define=goog.userAgent.product.ASSUME_CHROME=true",
            "--define=goog.NATIVE_ARRAY_PROTOTYPES=false",
            "--use_types_for_optimization=false",
        ],
        "ie": [
            "--define=goog.userAgent.ASSUME_IE=true",
        ],
        "ios": [
            "--define=goog.userAgent.ASSUME_MOBILE_WEBKIT=true",
        ],
        "firefox": [
            "--define=goog.userAgent.ASSUME_GECKO=true",
            "--define=goog.userAgent.product.ASSUME_FIREFOX=true",
        ],
    }
    for browser, defs in browser_defs.items():
        if len(browsers) > 0 and (browser == "*" or browser not in browsers):
            continue

        bin_name = name
        if browser != "*":
            bin_name = bin_name + "-" + browser

        closure_js_binary(
            name = bin_name,
            deps = [":" + exports_lib_name],
            defs = kwargs.get("defs", []) + defs,
            output_wrapper = wrapper,
            visibility = kwargs.get("visibility"),
        )
