load("//Config:configs.bzl", "app_binary_configs", "library_configs", "watch_binary_configs", "message_binary_configs", "pretty", "info_plist_substitutions", "bundle_identifier", "DEVELOPMENT_LANGUAGE")
load("//Config:buck_rule_macros.bzl", "apple_lib", "apple_test_lib", "apple_test_all")
load("//Config:buck_local.bzl", "buck_local_apple_asset_catalog", "buck_local_binary", "buck_local_bundle",
     "buck_local_workspace")

buck_local_apple_asset_catalog(
    name = "ExampleAppAssets",
    visibility = ["//App:"],
    app_icon = "AppIcon",
    dirs = ["Assets.xcassets"],
)

app_tests = [
    ":UnitTests",
    # TODO: Fix HostApp test and re-enable it.
    # ":UnitTestsWithHostApp",
]

ui_tests = [
    ":XCUITests",
]

# This is a list of all of our first-party libraries that are depended upon (directly or transitively) by this application.
# Every first-party library has an associated test target. We use this list to determine what test targets to run in CI.
first_party_library_dependencies = [
    "//Libraries/ASwiftModule:ASwiftModule",
    "//Libraries/Cpp1:Cpp1",
    "//Libraries/Objc1:Objc1",
    "//Libraries/ObjcAndSwift:ObjcAndSwift",
    "//Libraries/SecondSwiftModule:SecondSwiftModule",
    "//Libraries/SwiftAndObjc:SwiftAndObjc",
    "//Libraries/SwiftReliesOnCXX:SwiftReliesOnCXX",
    "//Libraries/SwiftWithAssets:SwiftWithAssets",
    "//Libraries/SwiftWithMLModel:SwiftWithMLModel",
    "//Libraries/SwiftWithPrecompiledDependency:SwiftWithPrecompiledDependency",
    "//Libraries/YetAnotherSwiftModule:YetAnotherSwiftModule",
]

prebuilt_frameworks = [
    "//PrebuiltFrameworks:AFNetworking",

    # Hack to enable project generation to work for the Carthage BUCK file which otherwise only has `prebuilt_apple_framework`s.
    "//PrebuiltFrameworks:PrebuiltFrameworksProjectGeneratorHack",
]

prebuilt_dynamic_frameworks = [
    # TODO: Comment out CardinalMobile for now until it supports M1 mac: https://github.com/braintree/braintree_ios/issues/564
    # "//Pods:CardinalMobile", # Prebuilt dylib
]

# Build Phase scripts need to be added as dependencies.
# These only get executed when building with Xcode, not Buck.
build_phase_scripts = [
    ":Hello_World",
    ":Bye_World",
]

apple_library(
    name = "ExampleAppLibrary",
    visibility = [
        "//App:",
        "//App/...",
    ],
    configs = library_configs(),
    swift_version = "4.0",
    srcs = [
        "ViewController.swift",
        "AppDelegate.swift",
        "LocalizationHelper.swift",
    ],
    tests = app_tests,
    deps = [
        # "//Pods:Braintree",
        "//Pods:CryptoSwift",
        "//Pods:PromiseKit",

        ":ExampleAppAssets",

        # Resources
        "//App/Resources:ExampleAppStringResources",
        "//App/Resources:StoryboardResources",
    ]
    + first_party_library_dependencies
    + prebuilt_dynamic_frameworks
    + build_phase_scripts,
)

buck_local_binary(
    name = "ExampleAppBinary",
    visibility = [
        "//App:",
        "//App/...",
    ],
    configs = app_binary_configs("ExampleApp"),
    swift_version = "4.0",
    srcs = [
        "BuckSupportFiles/Dummy.swift",
    ],
    native_xcode_deps = [":ExampleAppLibrary"],
    buck_local_deps=[
        "//BuckLocal:BuckLocal",
        "//BuckLocal:RemapDBGSourcePath",
    ],
)

# Defines a Build Phase script that gets executed before the "Compile Sources" step
xcode_prebuild_script(
    name = "Hello_World",
    cmd = '"${SRCROOT}/../scripts/sample.sh"',
    inputs = [],
    outputs = [],
    input_file_lists = [],
    output_file_lists = [],
)

# Defines a Build Phase script that gets executed after the "Compile Sources" step
xcode_postbuild_script(
    name = "Bye_World",
    cmd = 'echo Bye World!',
    inputs = [],
    outputs = [],
    input_file_lists = [],
    output_file_lists = [],
)

# This test bundles all unit test libraries into a single test target.
# Test targets can be slow to create in CI; creating only one can save significant time.
apple_test_all(
    name = "ExampleAppCITests",
    libraries = first_party_library_dependencies,
    additional_tests = app_tests,
    prebuilt_frameworks = prebuilt_frameworks + prebuilt_dynamic_frameworks,
)

buck_local_workspace(
    name = "workspace",
    workspace_name = "ExampleApp",
    src_target = ":ExampleApp",
    ui_test_target = ":XCUITests",
    native_xcode_scheme_actions={
        "Build": {
            "PRE_SCHEME_ACTIONS": ["echo 'Started'"],
            "POST_SCHEME_ACTIONS": ["echo 'Finished'"],
        },
    },
    action_config_names = {"profile": "Profile"},
)

buck_local_bundle(
    name = "ExampleApp",
    visibility = [
        "//App:",
    ],
    extension = "app",
    binary = ":ExampleAppBinary",
    product_name = "ExampleApp",
    info_plist = "Info.plist",
    info_plist_substitutions = info_plist_substitutions("ExampleApp"),
    native_xcode_deps=prebuilt_frameworks + [
        # For "#watch", https://buckbuild.com/rule/apple_bundle.html#deps
        ":ExampleWatchApp#watch",
        ":ExampleMessageExtension",
        ":ExampleWidgetExtension",
    ]
    + prebuilt_frameworks
    + prebuilt_dynamic_frameworks,
    buck_local_deps=prebuilt_frameworks,
)

apple_package(
    name = "ExampleAppPackage",
    bundle = ":ExampleApp",
)

### Watch App Begin ###
# Define the watch app in the same BUCK file as the binary into which the watch app will be installed.
# Xcode is finicky when it comes to how it embeds watch apps into main app bundles.  Watch apps are
# built into the `watchos` build directory, but Xcode only knows to look for the watch app binary in the `watchos`
# directory if the watch target is defined in the same `pbxproj` as the main app binary. If the key/value pair
# on the watch target `SDKROOT = watchos;` is not in the same `pbxproj` as the target that defines the main app
# binary, the Copy Files phase to embed the watch app and extension will search the `iphoneos` directory,
# and fail the build on generated Xcode projects.

# This is the code that runs on the iPhone and talks to the app running on the Watch.
apple_binary(
    name = "ExampleWatchAppExtensionBinary",
    srcs = glob([
        "WatchExtension/**/*.swift",
    ]),
    # Without specifying the target, buck will provide a wrong one,
    # which will cause compiler error.
    swift_compiler_flags = ["-target", "i386-apple-watchos4.0-simulator"],
    configs = watch_binary_configs("ExampleApp.WatchApp.Extension"),
    linker_flags = ["-e", "_WKExtensionMain"],
    frameworks = [
        "$SDKROOT/System/Library/Frameworks/CoreGraphics.framework",
        "$SDKROOT/System/Library/Frameworks/Foundation.framework",
        "$SDKROOT/System/Library/Frameworks/UIKit.framework",
        "$SDKROOT/System/Library/Frameworks/WatchConnectivity.framework",
        "$SDKROOT/System/Library/Frameworks/WatchKit.framework",
    ],
    headers = glob([
        "WatchExtension/**/*.h",
    ]),
)

apple_bundle(
    name = "ExampleWatchAppExtension",
    binary = ":ExampleWatchAppExtensionBinary",
    extension = "appex",
    info_plist = "WatchExtension/Info.plist",
    info_plist_substitutions = {
        "DEVELOPMENT_LANGUAGE": DEVELOPMENT_LANGUAGE,
        "EXECUTABLE_NAME": "ExampleWatchAppExtension",
        "PRODUCT_BUNDLE_IDENTIFIER": bundle_identifier("ExampleApp.WatchApp.Extension"),
        "WK_APP_BUNDLE_IDENTIFIER": bundle_identifier("ExampleApp.WatchApp"),
        "PRODUCT_NAME": "ExampleWatchAppExtension",
        "PRODUCT_MODULE_NAME": "ExampleWatchAppExtensionBinary",
    },
    xcode_product_type = "com.apple.product-type.watchkit2-extension",
)

# This is the code that runs on the Watch.
apple_binary(
    name = "ExampleWatchAppBinary",
    configs = watch_binary_configs("ExampleApp.WatchApp")
)

apple_bundle(
    name = "ExampleWatchApp",
    binary = ":ExampleWatchAppBinary",
    visibility = [
        "//App:",
    ],
    extension = "app",
    info_plist = "WatchApplication/Info.plist",
    info_plist_substitutions = {
        "DEVELOPMENT_LANGUAGE": DEVELOPMENT_LANGUAGE,
        "EXECUTABLE_NAME": "ExampleWatchApp",
        "PRODUCT_BUNDLE_IDENTIFIER": bundle_identifier("ExampleApp.WatchApp"),
        "WK_COMPANION_APP_BUNDLE_IDENTIFIER": bundle_identifier("ExampleApp"),
        "PRODUCT_NAME": "ExampleWatchApp",
    },
    xcode_product_type = "com.apple.product-type.application.watchapp2",
    deps = [
        ":ExampleWatchAppExtension",
        ":ExampleWatchAppResources",
    ],
)

apple_resource(
    name = "ExampleWatchAppResources",
    dirs = [],
    files = glob(["WatchApplication/**/*.storyboard"])
)
### Watch App End ###

### iMessage Extension Begin ###
# It is possible for an iMessage extension to be declared in a separate BUCK file.
# Declaring the iMessage extension in a separate `BUCK` file would result in that code living in a
# separate `pbxproj` file in the Buck-generate Xcode projects. Due to our learnings from the watch
# app (described above) we think it's safer to keep the iMessage extension in the same `BUCK` file
# that declares the main app bundle.

apple_binary(
    name = "ExampleMessageExtensionBinary",
    srcs = glob([
        "MessageExtension/**/*.swift",
    ]),
    configs = message_binary_configs("ExampleApp.MessageExtension"),

    # "-e _NSExtensionMain" tell linker this binary is app extension, so it won't fail due to missing _main
    # "-Xlinker -rpath -Xlinker @executable_path/../../Frameworks" tells the executable binary to
    # look for frameworks in ExampleApp.app/Frameworks instead of PlugIns, so that we don't need to have
    # the libSwift*.dylib in ExampleApp.app/PlugIns/*.appex/Frameworks
    linker_flags = [
        "-e",
        "_NSExtensionMain",
        "-Xlinker",
        "-rpath",
        "-Xlinker",
        "@executable_path/../../Frameworks",
    ],
)

apple_bundle(
    name = "ExampleMessageExtension",
    binary = ":ExampleMessageExtensionBinary",
    extension = "appex",
    info_plist = "MessageExtension/Info.plist",
    info_plist_substitutions = {
        "DEVELOPMENT_LANGUAGE": DEVELOPMENT_LANGUAGE,
        "EXECUTABLE_NAME": "ExampleMessageExtension",
        "PRODUCT_BUNDLE_IDENTIFIER": bundle_identifier("ExampleApp.MessageExtension"),
        "PRODUCT_NAME": "ExampleMessageExtension",
        "PRODUCT_MODULE_NAME": "ExampleMessageExtensionBinary",
    },
    deps = [
        ":ExampleMessageExtensionResources",
    ],
    xcode_product_type = "com.apple.product-type.app-extension.messages",
)

apple_resource(
    name = "ExampleMessageExtensionResources",
    dirs = [],
    files = glob(["MessageExtension/**/*.storyboard"])
)
### iMessage Extension End ###

### Widget Extension Begin ###
apple_binary(
    name = "ExampleWidgetExtensionBinary",
    srcs = glob([
        "WidgetExtension/**/*.swift",
    ]),
    configs = message_binary_configs("ExampleApp.WidgetExtension"),

    # Same linker flags as ExampleMessageExtensionBinary
    linker_flags = [
        "-e",
        "_NSExtensionMain",
        "-Xlinker",
        "-rpath",
        "-Xlinker",
        "@executable_path/../../Frameworks",
    ],
)

apple_bundle(
    name = "ExampleWidgetExtension",
    binary = ":ExampleWidgetExtensionBinary",
    extension = "appex",
    info_plist = "WidgetExtension/Info.plist",
    info_plist_substitutions = {
        "DEVELOPMENT_LANGUAGE": DEVELOPMENT_LANGUAGE,
        "EXECUTABLE_NAME": "ExampleWidgetExtension",
        "PRODUCT_BUNDLE_IDENTIFIER": bundle_identifier("ExampleApp.WidgetExtension"),
        "PRODUCT_NAME": "ExampleWidgetExtension",
        "PRODUCT_MODULE_NAME": "ExampleWidgetExtensionBinary",
        "PRODUCT_BUNDLE_PACKAGE_TYPE": "XPC!",
    },
    xcode_product_type = "com.apple.product-type.app-extension",
)
### Widget Extension End ###

### Tests Begin ###

apple_test_lib(
    name = "UnitTests",
    srcs = glob([
        "UnitTests/*.swift",
    ]),
    deps = [
        ":ExampleAppLibrary",
    ]
    + prebuilt_frameworks
    + prebuilt_dynamic_frameworks,
)

apple_test_lib(
    name = "UnitTestsWithHostApp",
    run_test_separately = True,
    test_host_app = ":ExampleApp",
    srcs = glob([
        "UnitTestsWithHostApp/*.swift",
    ]),
    deps = [
        ":ExampleAppLibrary",
    ],
)

# This test suite cannot be packaged with other tests due to a dependency on `fbxctest` instead of `xctool`.
apple_test_lib(
    name = "XCUITests",
    destination_specifier = {
        "name": "iPhone 8",
    },
    run_test_separately = True,
    # The `test_host_app` is launched first in the Simulator and needs to be an `apple_bundle` that is distinct from `ui_test_target_app`.
    test_host_app = ":XCUITestsHostApp",
    srcs = glob([
        "XCUITests/*.swift",
    ]),
    is_ui_test = True,
    # The `ui_test_target_app` field is not mentioned in Buck's official documentation, but it appears in its XCUITest fixtures.
    # https://github.com/facebook/buck/blob/97e1ef75be82ad5379e95c98cbb61ab656554a1b/test/com/facebook/buck/apple/testdata/apple_test_xcuitest/BUCK.fixture#L66
    ui_test_target_app = ":ExampleApp",
    labels = ['ui'],
    frameworks = [
        "$PLATFORM_DIR/Developer/Library/Frameworks/XCTest.framework",
        "$SDKROOT/System/Library/Frameworks/Foundation.framework",
        "$SDKROOT/System/Library/Frameworks/UIKit.framework",
    ],
)

# This test suite cannot be packaged with other tests due to a dependency on `fbxctest` instead of `xctool`.
apple_test_lib(
    name = "XCUITestsBuckLocal",
    destination_specifier = {
        "name": "iPhone 8",
    },
    run_test_separately = True,
    # The `test_host_app` is launched first in the Simulator and needs to be an `apple_bundle` that is distinct from `ui_test_target_app`.
    test_host_app = ":XCUITestsHostApp",
    srcs = glob([
        "XCUITests/*.swift",
    ]),
    is_ui_test = True,
    # The `ui_test_target_app` field is not mentioned in Buck's official documentation, but it appears in its XCUITest fixtures.
    # https://github.com/facebook/buck/blob/97e1ef75be82ad5379e95c98cbb61ab656554a1b/test/com/facebook/buck/apple/testdata/apple_test_xcuitest/BUCK.fixture#L66
    ui_test_target_app = ":ExampleAppBuckLocal",
    labels = ['ui'],
    frameworks = [
        "$PLATFORM_DIR/Developer/Library/Frameworks/XCTest.framework",
        "$SDKROOT/System/Library/Frameworks/Foundation.framework",
        "$SDKROOT/System/Library/Frameworks/UIKit.framework",
    ],
)

# A very thin host app to enable running :XCUITests
apple_bundle(
    name = "XCUITestsHostApp",
    visibility = [
        "//App:XCUITests",
        "//App:XCUITestsBuckLocal",
    ],
    extension = "app",
    binary = ":XCUITestsHostAppBinary",
    product_name = "XCUITestsHostApp",
    info_plist = "Info.plist",
    info_plist_substitutions = info_plist_substitutions("XCUITestsHostApp"),
)

apple_binary(
    name = "XCUITestsHostAppBinary",
    visibility = [
        "//App:XCUITestsHostApp",
    ],
    configs = app_binary_configs("XCUITestsHostAppBinary"),
    swift_version = "4.0",
    srcs = [
        "BuckSupportFiles/DummyAppDelegate.swift",
    ],
)

### Tests End ###