import Foundation

@objc public class Bar: NSObject {
  @objc public class func printSwiftStruct() {
    Swift.print(TestLib(version: 0, name: "TestLib"))
  }
}


struct TestLib {
  let version: Int
  let name: String
}


import Foundation

@objc public class Dummy: NSObject {
  @objc public class func hello() {
    Swift.print("hello")
  }
}


import Foo

class Dummy {
  func test() {
    let hello = Hello()
    Swift.print(hello.hello())
  }
}


class Dummy {
  func test() {
    let hello = Hello()
    Swift.print(hello.hello())
  }
}


import Foo

class Bar {
  func bar() {
    let foo = Foo()
    foo.hello()
  }
}


public class Foo {
  public init() {}
  public func hello() {
    Swift.print("hello")
  }
}


@objc public class Foo: FooBase {
  @objc public class func printString() {
    Swift.print("string")
  }
}


import Foundation

@objc public class Dummy: NSObject {
  @objc public class func hello() -> String {
    return "hello"
  }
}


import Foundation

@objc public class Dummy: NSObject {
  @objc public class func magicValue() -> Int {
    return 42
  }
}


import XCTest

class LibTest: XCTestCase {
  public func testDummy() {}
}


import XCTest

class LibTest: XCTestCase {
  func testLib() {
    XCTAssertEqual(42, Lib().magicValue(), "Magic value expected to match")
  }
}


import XCTest
import Lib

class LibTest: XCTestCase {
  func testLib() {
    XCTAssertEqual("hello", Dummy.hello(), "Hello expected to be true")
  }
}


//
//  AppDelegate.swift
//  AppClipApp
//
//  Created by Lucas Marcal on 08/10/20.
//

import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        self.window = UIWindow(frame: UIScreen.main.bounds)
        self.window?.rootViewController = ViewController()
        self.window?.makeKeyAndVisible()
        return true
    }
}


import Foundation
import UIKit

let argc = CommandLine.argc
let argv = UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc))

UIApplicationMain(argc, argv, nil, NSStringFromClass(AppDelegate.self))


//
//  ViewController.swift
//  AppClipApp
//
//  Created by Lucas Marcal on 08/10/20.
//

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .yellow
    }
}


//
//  AppDelegate.swift
//  Clip
//
//  Created by Lucas Marcal on 07/10/20.
//

import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        self.window = UIWindow(frame: UIScreen.main.bounds)
        self.window?.rootViewController = UIViewController()
        self.window?.rootViewController?.view.backgroundColor = .red
        self.window?.makeKeyAndVisible()
        return true
    }
}


// Copyright 2014-present Facebook, Inc.

import Library
import PathsLibrary

func test() {
    FunctionA()
    FunctionB()
    FunctionC()
    FunctionFromPathsLibrary()
}


import Foundation

@objc public class Hello : NSObject {
    public class func sayHello(name: String) {
        Greeter.sayHello(name)
    }
}


import Foundation

@objc public class SwiftClass: NSObject {
    @objc public func printStuff() {
        NSLog("HAIFROMSWIFT")
    }
}


import Foundation
import Dummy

func addTwo(to i: Int) -> Int {
	let _ = HelloWorldClass()
	return 1
}


/*
 * Copyright 2015-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

import UIKit

@UIApplicationMain
final class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let vc = UIViewController()
        vc.view.backgroundColor = UIColor.red
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.rootViewController = vc

        self.window = window
        window.makeKeyAndVisible()

        return true
    }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit

/// Represents a parameter-less action.
public struct Action {
  /// The type erased handler representing the action.
  private let handler: () -> Void

  fileprivate init(handler: @escaping () -> Void) {
    self.handler = handler
  }

  init<View: CKSwift.View>(handler: @escaping (View) -> Void) {
    let actionResolver = ScopedResponderActionResolver<View>()

    self.handler = {
      if let view = actionResolver.targetView {
        handler(view)
      }
    }
  }

  init<View: CKSwift.View>(handler: @escaping (View) -> () -> Void) {
    self.init() { view in
      handler(view)()
    }
  }

  /// Creates an action from a target + handler. Use this when the target of an action isn't a view/component.
  public init<Target: AnyObject>(target: Target, handler: @escaping (Target) -> () -> Void) {
    self.handler = { [weak weakTarget = target] in
      guard let strongTarget = weakTarget else { return }
      handler(strongTarget)()
    }
  }

  /// Invokes the action.
  func invoke() {
    handler()
  }

  /// The objective-c bridgeable representation of a parameter-less action.
  public var swiftBridge: Action_SwiftBridge {
    return {
      self.invoke()
    }
  }
}

/// Represents an action which can be invoked at a later date. An action will always
/// be invoked on the first non-null generation of a component.
public struct ActionWith<Param> {
  /// The type erased handler representing the action.
  private let handler: (Param) -> Void

  private init(handler: @escaping (Param) -> Void) {
    self.handler = handler
  }

  init<View: CKSwift.View>(handler: @escaping (View, Param) -> Void) {
    let actionResolver = ScopedResponderActionResolver<View>()

    self.handler = { param in
      if let view = actionResolver.targetView {
        handler(view, param)
      }
    }
  }

  init<View: CKSwift.View>(handler: @escaping (View) -> (Param) -> Void) {
    self.init() { view, param in
      handler(view)(param)
    }
  }

  /// Creates an action from a target + handler. Use this when the target of an action isn't a view/component.
  public init<Target: AnyObject>(target: Target, handler: @escaping (Target) -> (Param) -> Void) {
    self.handler = { [weak weakTarget = target] param in
      guard let strongTarget = weakTarget else { return }
      handler(strongTarget)(param)
    }
  }

  /// Invokes the action.
  /// - Parameter param: The parameter to pass to the action.
  public func invoke(_ param: Param) {
    handler(param)
  }

  /// The objective-c bridgeable representation of a parametrized action.
  public var swiftBridge: ActionWithId_SwiftBridge {
    return { aParam in
      guard let param = aParam as? Param else {
        fatalError("Expecting to find \(Param.self) but got \(type(of: aParam))")
      }
      self.invoke(param)
    }
  }

  /// The objective-c bridgeable representation of a parametrized action (to be removed soon)
  public var swiftBridgeWithType: ActionWithId_SwiftBridge {
    swiftBridge
  }

  /// Demotes an action with a parameter to a parameter less param.
  /// - Parameter param: The value to pass to the action handler.
  /// - Returns: The parameterless `Action`.
  public func demote(passing param: Param) -> Action {
    Action {
      handler(param)
    }
  }
}

/// Marker protocol to indicate that a view can supply actions.
public protocol Actionable : TreeNodeLinkableView { }

extension View where Self: Actionable {
  public func onAction<Param>(_ handler: @escaping (Self, Param) -> Void) -> ActionWith<Param> {
    ActionWith(handler: handler)
  }

  public func onAction<Param>(_ handler: @escaping (Self) -> (Param) -> Void) -> ActionWith<Param> {
    ActionWith(handler: handler)
  }

  public func onAction(_ handler: @escaping (Self) -> Void) -> Action {
    Action(handler: handler)
  }

  public func onAction(_ handler: @escaping (Self) -> () -> Void) -> Action {
    Action(handler: handler)
  }

  /// Creates a `Action` which receives a param not specified by the caller but on creation.
  public func onAction<Param>(_ handler: @escaping (Self) -> (Param) -> Void, passing param: Param) -> Action {
    Action() { view in
      handler(view)(param)
    }
  }
}

/// Artificial object which helps to share responder related logic
private struct ScopedResponderActionResolver<View: CKSwift.View> {
  private let responder: ScopedResponder
  private let key: ScopedResponderKey

  init() {
    var responder: ScopedResponder?
    var key: ScopedResponderKey = -1

    guard CKSwiftInitializeAction(SwiftComponent<View>.self, &responder, &key),
          let scopedResponder = responder,
          key != -1 else {
      fatalError("Attempting to initialise action outside of the right body function")
    }

    self.responder = scopedResponder
    self.key = key
  }

  var targetView: View? {
    guard let untypedComponent = responder.responder(forKey: key) as? Component else { return nil }
    guard let component = untypedComponent as? SwiftComponent<View> else {
      fatalError("Expecting \(SwiftComponent<View>.self) when invoking action but got \(untypedComponent) instead")
    }
    return component.view
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import UIKit

extension Array where Element: CAAnimation {
  func groupAnimationOrNil(fillMode: CAMediaTimingFillMode = .backwards) -> CAAnimation? {
    guard count > 1 else {
      return first
    }

    let animationGroup = CAAnimationGroup()
    animationGroup.animations = self
    animationGroup.fillMode = fillMode
    return animationGroup
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit

@dynamicMemberLookup
@propertyWrapper
public struct Binding<Value> {
  private let get: () -> Value
  private let set: (Value) -> Void

  public init(get: @escaping () -> Value, set: @escaping (Value) -> Void) {
    self.get = get
    self.set = set
  }

  public var wrappedValue: Value {
    get {
      get()
    }
    nonmutating set {
      set(newValue)
    }
  }

  public var projectedValue: Binding<Value> {
    self
  }

  public subscript<T>(dynamicMember keyPath: WritableKeyPath<Value, T>) -> Binding<T> {
    Binding<T>(get: {
      self.wrappedValue[keyPath: keyPath]
    }, set: {
      self.wrappedValue[keyPath: keyPath] = $0
    })
  }
}

extension Binding : Equatable where Value : Equatable {
  static public func ==(lhs: Binding, rhs: Binding) -> Bool {
    // That will be enough for now, but should two bindings pointing to different state
    // of the same values be equal?
    lhs.wrappedValue == rhs.wrappedValue
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import ComponentKit
import UIKit

// Re-export the type to avoid importing ComponentKit directly and ambiguities in type lookup
public typealias SizeRange = ComponentKit.SizeRange
public typealias ComponentHostingView = ComponentKit.ComponentHostingView
public typealias ComponentHostingViewDelegate = ComponentKit.ComponentHostingViewDelegate
public typealias Component = ComponentKit.Component
public typealias CompositeComponent = ComponentKit.CompositeComponent
public typealias FlexboxComponent = ComponentKit.FlexboxComponent


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import ComponentKit
import UIKit

// MARK: - CKComponent

public extension Component {
  /// Creates a new component.
  /// - Parameters:
  ///   - view: The view configuration to be used by the component.
  ///   - size: The size to be used by the component.
  convenience init(view: ViewConfiguration? = nil, size: ComponentSize? = nil) {
    self.init(__swiftView: view?.viewConfiguration, swiftSize: size?.componentSize)
  }
}

// MARK: - CKCompositeComponent

public extension CompositeComponent {
  /// Create a new composite component.
  /// - Parameters:
  ///   - view: The view configuration to be used by the component.
  ///   - component: The component to wrap.
  convenience init(view: ViewConfiguration? = nil, component: Mountable) {
    self.init(__swiftView: view?.viewConfiguration, component: component)
  }

  /// Creates a new composite component, conditionally.
  /// - Parameters:
  ///   - view: The view configuration to be used by the component.
  ///   - component: The optional component to wrap.
  convenience init?(view: ViewConfiguration? = nil, component: Mountable?) {
    guard let component = component else { return nil }
    self.init(__swiftView: view?.viewConfiguration, component: component)
  }
}

// MARK: Component

extension Component : ComponentInflatable {
  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    // When a non-view issued component is built we need to check the model for:
    // animations & lifecycle callbacks. The component is to be conditionally
    // wrapped.
    if let model = model, model.isEmpty == false {
      return CKSwiftComponent(
        swiftView: nil,
        swiftSize: nil,
        child: self,
        model: model.toSwiftBridge())
    } else {
      return self
    }
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit

// MARK: ComponentBuilder

@_functionBuilder
public struct ComponentBuilder {
  // TODO: buildDo

  public static func buildBlock(_ component: Component) -> Component {
    component
  }

  public static func buildExpression<Inflatable: ComponentInflatable>(_ inflatable: Inflatable) -> Component {
    inflatable.inflateComponent(with: nil)
  }

  public static func buildEither(first: Component) -> Component {
    first
  }

  public static func buildEither(second: Component) -> Component {
    second
  }

  public static func buildOptional(_ component: Component?) -> Component {
    component ?? EmptyComponent.shared
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import UIKit

final class ObjCProviderWrapper: NSObject {
  let provider: () -> Component

  init(provider: @escaping () -> Component) {
    self.provider = provider
  }
}

public final class ComponentHostingViewController: UIViewController {
  private let rootComponentProvider: () -> Component
  private let hostingView: ComponentHostingView<NSObject, ObjCProviderWrapper>
  private let backgroundColor: UIColor?

  public init(backgroundColor: UIColor? = .white, @ComponentBuilder rootComponentProvider: @escaping () -> Component) {
    self.backgroundColor = backgroundColor
    self.rootComponentProvider = rootComponentProvider
    hostingView = ComponentHostingView(componentProvider: { _, wrapper in wrapper!.provider() },
                                       sizeRangeProviderBlock: { size in SizeRange(minSize: .zero, maxSize: size) })
    super.init(nibName: nil, bundle: nil)
  }

  required init(coder: NSCoder) {
    fatalError()
  }

  public override func viewDidLoad() {
    view.backgroundColor = backgroundColor

    hostingView.updateContext(ObjCProviderWrapper(provider: rootComponentProvider), mode: .synchronous)

    view.addSubview(hostingView)
  }

  public override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    hostingView.hostingViewWillAppear()
  }

  public override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    hostingView.hostingViewDidDisappear()
  }

  public override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    hostingView.frame = view.bounds
  }
}

extension ComponentHostingViewController: ComponentHostingViewDelegate {
  public func componentHostingViewDidInvalidateSize(_: UIView) {
    view.setNeedsLayout()
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import ComponentKit

/// Represents a component desired size relative to the size of its parent.
public struct ComponentSize: Hashable {
  /// The Objective-C bridgeable type.
  public let componentSize: ComponentSizeSwiftBridge

  /// Creates a new component size.
  /// - Parameter size: The CKSize to derive the size from.
  public init(size: CGSize) {
    componentSize = ComponentSizeSwiftBridge(size: size)
  }

  /// Creates a new component size.
  /// - Parameters:
  ///   - width: The component size's width.
  ///   - height: The component size's height.
  ///   - minWidth: The component size's min width.
  ///   - minHeight: The component size's min height.
  ///   - maxWidth: The component size's max width.
  ///   - maxHeight: The component size's max height.
  public init(width: Dimension? = nil,
              height: Dimension? = nil,
              minWidth: Dimension? = nil,
              minHeight: Dimension? = nil,
              maxWidth: Dimension? = nil,
              maxHeight: Dimension? = nil) {
    componentSize = ComponentSizeSwiftBridge(width: width?.dimension,
                                             height: height?.dimension,
                                             minWidth: minWidth?.dimension,
                                             minHeight: minHeight?.dimension,
                                             maxWidth: maxWidth?.dimension,
                                             maxHeight: maxHeight?.dimension)
  }
}

extension ComponentSize: CustomStringConvertible {
  public var description: String { return componentSize.description }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import UIKit
import ComponentKit

// MARK: ComponentView

/// `ComponentView` is the `View` based `Component` primitive. Will become a leaf component.
/// As opposed to `Component`, it has the benefit to being a value type.
public struct ComponentView<UIViewType: UIView> : View, ViewAttributeAssignable {
  public init() { }

  public typealias AttributeBuilder = ViewConfigurationAttributeBuilder<UIViewType>
  public init(@AttributeBuilder attributes: () -> [AttributeBuilder.Directive]) {
    consume(attributes)
  }

  // MARK: ViewAttributeAssignable

  public var attributes: [ViewConfiguration.Attribute<UIViewType>] = []
  public var layerAttributes: [ViewConfiguration.LayerAttribute] = []

  // MARK: ComponentInflatable

  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    if let model = model, model.isEmpty == false {
      return CKSwiftComponent(
        swiftView: viewConfiguration.viewConfiguration,
        swiftSize: nil,
        child: nil,
        model: model.toSwiftBridge()
      )
    } else {
      return Component(view: viewConfiguration)
    }
  }
}

/// `WrapperComponentView` is the `View` based `CompositeComponent` primitive.
/// As opposed to `CompositeComponent`, it has the benefit to being a value type.
public struct WrapperComponentView<UIViewType: UIView> : View, ViewAttributeAssignable {
  let component: () -> Component

  public typealias AttributeBuilder = ViewConfigurationAttributeBuilder<UIViewType>
  public init(
    @AttributeBuilder attributes: () -> [AttributeBuilder.Directive],
    @ComponentBuilder component: @escaping () -> Component) {
    self.component = component
    consume(attributes)
  }

  // MARK: ViewAttributeAssignable

  public var attributes: [ViewConfiguration.Attribute<UIViewType>] = []
  public var layerAttributes: [ViewConfiguration.LayerAttribute] = []

  // MARK: ComponentInflatable

  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    if let model = model, model.isEmpty == false {
      return CKSwiftComponent(
        swiftView: viewConfiguration.viewConfiguration,
        swiftSize: nil,
        child: self.component(),
        model: model.toSwiftBridge()
      )
    } else {
      return CompositeComponent(
        view: viewConfiguration,
        component: self.component())
    }
  }
}

fileprivate extension ViewAttributeAssignable {
  mutating func consume(_ attributes: () -> [ViewConfigurationAttributeBuilder<UIViewType>.Directive]) {
    attributes()
      .forEach {
        switch $0 {
        case let .attribute(attribute):
          self.attributes.append(attribute)
        case let .layerAttribute(attribute):
          self.layerAttributes.append(attribute)
        }
    }
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import ComponentKit

/// A dimension relative to constraints to be provided in the future.
public enum Dimension: Hashable {
  case points(CGFloat)
  case percent(CGFloat)
  case auto

  /// The Objective-C bridgeable type.
  public var dimension: DimensionSwiftBridge {
    switch self  {
    case let .points(points):
      return DimensionSwiftBridge(points: points)
    case let .percent(percent):
      return DimensionSwiftBridge(percent: percent)
    case .auto:
      return DimensionSwiftBridge.autoInstance()
    }
  }
}

extension Dimension: CustomStringConvertible {
  public var description: String { return dimension.description }
}

extension Dimension: ExpressibleByFloatLiteral {
  public init(floatLiteral value: Double) {
    self = .points(CGFloat(value))
  }
}

extension Dimension: ExpressibleByIntegerLiteral {
  public init(integerLiteral value: Int) {
    self = .points(CGFloat(value))
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import ComponentKit
import UIKit

public extension FlexboxComponent.Child {
  convenience init(
    spacingBefore: CGFloat = 0,
    spacingAfter: CGFloat = 0,
    flexGrow: CGFloat = 0,
    flexShrink: CGFloat = 0,
    alignSelf: AlignSelf = .auto,
    flexBasis: Dimension? = nil,
    zIndex: NSInteger = 0,
    sizeConstraints: ComponentSize? = nil,
    useTextRounding: Bool = false,
    useHeightAsBaseline: Bool = false,
    @ComponentBuilder component: () -> Component?) {
    self.init(
      __component: component(),
      spacingBefore: spacingBefore,
      spacingAfter: spacingAfter,
      flexGrow: flexGrow,
      flexShrink: flexShrink,
      swiftFlexBasis: flexBasis?.dimension,
      alignSelf: alignSelf,
      zIndex: zIndex,
      sizeConstraints: sizeConstraints?.componentSize,
      useTextRounding: useTextRounding,
      useHeightAsBaseline: useHeightAsBaseline)
  }
}

public extension FlexboxComponent.Style {
  convenience init(
    direction: Direction = .column,
    spacing: CGFloat = 0,
    justifyContent: JustifyContent = .start,
    alignItems: AlignItems = .stretch,
    alignContent: AlignContent = .start,
    wrap: Wrap = .noWrap,
    layoutDirection: LayoutDirection = .applicationDirection,
    useDeepYogaTrees: Bool = false) {
    self.init(
      __direction: direction,
      spacing: spacing,
      justifyContent: justifyContent,
      alignItems: alignItems,
      alignContent: alignContent,
      wrap: wrap,
      layoutDirection: layoutDirection,
      useDeepYogaTrees: useDeepYogaTrees
    )
  }
}

@_functionBuilder
public struct FlexboxChildrenBuilder {
  public static func buildExpression(_ child: FlexboxComponent.Child) -> FlexboxComponent.Child {
    child
  }

  public static func buildExpression<C: Sequence>(_ collection: C) -> [FlexboxComponent.Child] where C.Element == FlexboxComponent.Child {
    Array(collection)
  }

  public static func buildExpression<C : Sequence, Inflatable: ComponentInflatable>(_ collection: C) -> [FlexboxComponent.Child] where C.Element == Inflatable {
    collection.map { inflatable in
      FlexboxComponent.Child {
        inflatable
      }
    }
  }

  public static func buildExpression<Inflatable : ComponentInflatable>(_ inflatable: Inflatable) -> FlexboxComponent.Child {
    FlexboxComponent.Child {
      inflatable
    }
  }

  public static func buildBlock<C: Sequence>(_ children: C...) -> [FlexboxComponent.Child] where C.Element == FlexboxComponent.Child {
    children.flatMap { $0 }
  }

  public static func buildBlock(_ children: FlexboxComponent.Child...) -> [FlexboxComponent.Child] {
    children
  }

  public static func buildOptional(_ children: [FlexboxComponent.Child]?) -> [FlexboxComponent.Child] {
    children.map {
      Array($0)
    } ?? []
  }

  public static func buildEither<C: Sequence>(first collection: C) -> [FlexboxComponent.Child] where C.Element == FlexboxComponent.Child {
    Array(collection)
  }

  public static func buildEither<C: Sequence>(second collection: C) -> [FlexboxComponent.Child] where C.Element == FlexboxComponent.Child {
    Array(collection)
  }

  public static func buildLimitedAvailability<C: Sequence>(_ collection: C) -> [FlexboxComponent.Child] where C.Element == FlexboxComponent.Child {
    Array(collection)
  }

  public static func buildDo<C: Sequence>(_ collection: C) -> [FlexboxComponent.Child] where C.Element == FlexboxComponent.Child {
    Array(collection)
  }
}

public extension FlexboxComponent {
  convenience init(
    view: ViewConfiguration? = nil,
    direction: Style.Direction = .column,
    spacing: CGFloat = 0,
    justifyContent: Style.JustifyContent = .start,
    alignItems: Style.AlignItems = .stretch,
    alignContent: Style.AlignContent = .start,
    wrap: Style.Wrap = .noWrap,
    layoutDirection: Style.LayoutDirection = .applicationDirection,
    useDeepYogaTrees: Bool = false,
    size: ComponentSize? = nil,
    @FlexboxChildrenBuilder childrenBuilder: () -> [FlexboxComponent.Child]
  ) {
    self.init(
      __swiftView: view?.viewConfiguration,
      swiftStyle: Style(
        direction: direction,
        spacing: spacing,
        justifyContent: justifyContent,
        alignItems: alignItems,
        alignContent: alignContent,
        wrap: wrap,
        layoutDirection: layoutDirection,
        useDeepYogaTrees: useDeepYogaTrees),
      swiftSize: size?.componentSize,
      swiftChildren: childrenBuilder()
    )
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import UIKit
import ComponentKit

extension ViewConfiguration.Attribute {
  /// Creates a view configuration attribute linked to tap gesture recognizer.
  /// - Parameter tapAction: Action to invoke when the gesture fires..
  init(gesture: ComponentViewAttributeSwiftBridge.Gesture, action: ActionWith<UIGestureRecognizer>) {
    self.init(componentViewAttribute: ComponentViewAttributeSwiftBridge(gesture: gesture, swiftAction: action.swiftBridgeWithType))
  }
}

public struct TapAction {
  let action: ActionWith<UIGestureRecognizer>
}

public struct PanAction {
  let action: ActionWith<UIGestureRecognizer>
}

public struct LongPressAction {
  let action: ActionWith<UIGestureRecognizer>
}

extension View where Self: Actionable {
  public func onTap(_ handler: @escaping (Self, UIGestureRecognizer) -> Void) -> TapAction {
    TapAction(action: onAction(handler))
  }

  public func onTap(_ handler: @escaping (Self) -> (UIGestureRecognizer) -> Void) -> TapAction {
    TapAction(action: onAction(handler))
  }

  public func onPan(_ handler: @escaping (Self, UIGestureRecognizer) -> Void) -> PanAction {
    PanAction(action: onAction(handler))
  }

  public func onPan(_ handler: @escaping (Self) -> (UIGestureRecognizer) -> Void) -> PanAction {
    PanAction(action: onAction(handler))
  }

  public func onLongPress(_ handler: @escaping (Self, UIGestureRecognizer) -> Void) -> LongPressAction {
    LongPressAction(action: onAction(handler))
  }

  public func onLongPress(_ handler: @escaping (Self) -> (UIGestureRecognizer) -> Void) -> LongPressAction {
    LongPressAction(action: onAction(handler))
  }
}

public extension ViewConfigurationAttributeBuilder {
  static func buildExpression(_ tapAction: TapAction) -> Directive {
    buildExpression(ViewConfiguration.Attribute<View>(gesture: .tap, action: tapAction.action))
  }

  static func buildExpression(_ panAction: PanAction) -> Directive {
    buildExpression(ViewConfiguration.Attribute<View>(gesture: .pan, action: panAction.action))
  }

  static func buildExpression(_ longPressAction: LongPressAction) -> Directive {
    buildExpression(ViewConfiguration.Attribute<View>(gesture: .longPress, action: longPressAction.action))
  }
}

extension ViewAttributeAssignable where Self: ComponentInflatable {

  public func action(_ tapAction: TapAction) -> Self {
    var copy = self
    copy.attributes.append(ViewConfiguration.Attribute(gesture: .tap, action: tapAction.action))
    return copy
  }

  public func action(_ panAction: PanAction) -> Self {
    var copy = self
    copy.attributes.append(ViewConfiguration.Attribute(gesture: .pan, action: panAction.action))
    return copy
  }

  public func action(_ longPressAction: LongPressAction) -> Self {
    var copy = self
    copy.attributes.append(ViewConfiguration.Attribute(gesture: .longPress, action: longPressAction.action))
    return copy
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit

@propertyWrapper
// TODO: Use read/write reflection mechanism
public struct State<Value> : TreeNodeLinkable {
  private let store: TreeNodeValueStore<Value>

  public init(wrappedValue valueProvider: @escaping @autoclosure () -> Value) {
    self.store = TreeNodeValueStore(valueProvider: valueProvider)
  }

  /// Should only be called during component build or on the main thread thereafter
  public var wrappedValue: Value {
    get {
      store.get()
    }

    /// Should only be called on the main thread
    nonmutating set {
      store.set(newValue)
    }
  }

  public var projectedValue: Binding<Value> {
    return Binding(get: { self.wrappedValue }, set: { self.wrappedValue = $0 })
  }

  // MARK: TreeNodeLinkable

  func link(with node: CKTreeNode, at index: Int) {
    store.link(with: node, at: index)
  }
}

extension State : Equatable where Value : Equatable {
  static public func ==(lhs: State, rhs: State) -> Bool {
    lhs.wrappedValue == rhs.wrappedValue
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit

public struct SwiftComponentModel {
  struct LifecycleCallbacks {
    typealias Callback = @convention(block) () -> Void

    var didInit: [Callback] = []
    var willMount: [Callback] = []
    var didUnmount: [Callback] = []
    var willDispose: [Callback] = []

    var isEmpty: Bool {
      didUnmount.isEmpty && willMount.isEmpty && didUnmount.isEmpty && willDispose.isEmpty
    }
  }

  struct Animations {
    var animations: [CAAnimation] = []
    var initialMount: [CAAnimation] = []
    var finalUnmount: [CAAnimation] = []

    var isEmpty: Bool {
      animations.isEmpty && initialMount.isEmpty && finalUnmount.isEmpty
    }
  }

  func toSwiftBridge() -> SwiftComponentModelSwiftBridge? {
    guard isEmpty == false else { return nil }
    return SwiftComponentModelSwiftBridge(
      animation: animations.animations.groupAnimationOrNil(),
      initialMountAnimation: animations.initialMount.groupAnimationOrNil(),
      finalUnmountAnimation: animations.finalUnmount.groupAnimationOrNil(fillMode: .forwards),
      didInitCallbacks: lifecycleCallbacks.didInit.isEmpty ? nil : lifecycleCallbacks.didInit,
      willMountCallbacks: lifecycleCallbacks.willMount.isEmpty ? nil : lifecycleCallbacks.willMount,
      didUnmountCallbacks: lifecycleCallbacks.didUnmount.isEmpty ? nil : lifecycleCallbacks.didUnmount,
      willDisposeCallbacks: lifecycleCallbacks.willDispose.isEmpty ? nil : lifecycleCallbacks.willDispose
    )
  }

  var lifecycleCallbacks = LifecycleCallbacks()
  var animations = Animations()

  var isEmpty: Bool {
    lifecycleCallbacks.isEmpty && animations.isEmpty
  }
}

class SwiftComponent<View: CKSwift.View> : CKSwiftComponent {
  let view: View

  init(_ view: View, body: Component? = nil, viewConfiguration: ViewConfiguration? = nil, size: ComponentSize? = nil, model: SwiftComponentModel?) {
    self.view = view
    super.init(
      swiftView: viewConfiguration?.viewConfiguration,
      swiftSize: size?.componentSize,
      child: body,
      model: model?.toSwiftBridge())
  }

  init(_ shellComponent: SwiftComponent<View>, body: Component? = nil) {
    self.view = shellComponent.view
    super.init(
      fromShellComponent: shellComponent,
      child: body
    )
  }

  override var typeName: UnsafePointer<Int8> {
    // Swift components can either be a `SwiftComponent<View>`, `SwiftReusableComponent<View>` or
    // `SwiftReusableLeafComponent<View>` based on the capabilities of `View`.
    // Always use `SwiftComponent<View>` as the `typeName` so that CKSwift's infra doesn't have
    // to know which concrete subclass is being used. Really what's important is the `View` part
    // which suffices to identify the component.
    class_getName(SwiftComponent<View>.self)
  }
}

class SwiftReusableBaseComponent<View: ReusableView> : SwiftComponent<View>, ReusableComponentProtocol {
  var componentIdentifier: Any? {
    view.id
  }

  func didReuseComponent(_ component: ReusableComponentProtocol) {
    fatalError("Should never be called")
  }

  func shouldComponentUpdate(_ untypedComponent: ReusableComponentProtocol) -> Bool {
    guard let component = untypedComponent as? SwiftReusableBaseComponent<View> else {
      fatalError("Attempting to reuse a component of a different type: \(type(of: untypedComponent))")
    }

    return view != component.view
  }

  func clone() -> Self {
    fatalError("-clone should never be called on `SwiftReusableBaseComponent`.")
  }
}

final class SwiftReusableComponent<View: ReusableView> : SwiftReusableBaseComponent<View> where View.Body == Component {
  override func clone() -> Self {
    // TODO: Reuse logic
    view.linkPropertyWrappersWithScopeHandle()

    defer {
      CKSwiftPopClass()
    }
    return SwiftReusableComponent(self, body: view.body) as! Self
  }
}

final class SwiftReusableLeafComponent<View: ReusableView> : SwiftReusableBaseComponent<View> where View.Body == Never {
  override func clone() -> Self {
    // TODO: Reuse logic
    view.linkPropertyWrappersWithScopeHandle()

    defer {
      CKSwiftPopClass()
    }
    return SwiftReusableLeafComponent(self) as! Self
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit

/// Marker protocol for views indicating that a backing tree node is necessary.
public protocol TreeNodeLinkableView { }

protocol TreeNodeLinkable {
  func link(with node: CKTreeNode, at index: Int)
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit

final class TreeNodeValueStore<Value> {
  private let valueProvider: () -> Value
  private var link: (node: CKTreeNode, index: Int, value: Value)?

  init(valueProvider: @escaping () -> Value) {
    self.valueProvider = valueProvider
  }

  @discardableResult
  func link(with node: CKTreeNode, at index: Int) -> Bool {
    let wasFirstInit = CKSwiftInitializeState(node, index, valueProvider)
    let untypedValue = CKSwiftFetchState(node, index)
    guard let value = untypedValue as? Value else {
      preconditionFailure("Unexpected value \(String(describing: untypedValue))")
    }

    link = (node, index, value)
    return wasFirstInit
  }

  func get() -> Value {
    guard let link = link else {
      preconditionFailure("Attempting to read state before scope handle location linked.")
    }
    return link.value
  }

  func set(_ value: Value) {
    guard let nonNilLink = link else {
      preconditionFailure("Attempting to write state before `-body`.")
    }

    link!.value = value
    CKSwiftUpdateState(nonNilLink.node, nonNilLink.index, value)
  }

  var isLinked: Bool {
    link != nil
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit

// MARK: ComponentInflatable

/// Can be inflated to a `Component` given a `SwiftComponentModel`.
/// Anything that is `ComponentInflatable` can be added to a CKSwift view hierarchy.
public protocol ComponentInflatable {
  func inflateComponent(with model: SwiftComponentModel?) -> Component
}

// MARK: View

public protocol View : ComponentInflatable {
  associatedtype Body
  @ComponentBuilder var body: Body { get }
}

extension View where Self.Body == Never {
  public var body: Never {
    // Leaf views don't return
    fatalError("Attempting to call .body on a leaf view")
  }
}

public protocol ViewIdentifiable : TreeNodeLinkableView {
  associatedtype ID: Hashable
  var id: ID { get }
}

public typealias ReusableView = View & ViewIdentifiable & Equatable

// MARK: Non-leaf component

extension View where Self.Body == Component {
  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    // TODO: Reuse logic

    let hasScopeHandle = linkPropertyWrappersWithScopeHandle(
      forceRequireNode: model?.isEmpty == false)

    if hasScopeHandle == false {
      // If the current view doesn't require a scope handle and there is no view configuration
      // simply inflate the body to reduce the number of components generated. aka Stateless.
      return body.inflateComponent(with: model)
    }

    defer {
      if hasScopeHandle {
        CKSwiftPopClass()
      }
    }

    return SwiftComponent(
      self,
      body: body,
      model: model
    )
  }
}

extension View where Self: ViewIdentifiable, Self.Body == Component {
  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    // TODO: Reuse logic
    linkPropertyWrappersWithScopeHandle()

    defer {
      CKSwiftPopClass()
    }

    return SwiftComponent(
      self,
      body: body,
      model: model
    )
  }
}

extension View where Self: ViewIdentifiable & Equatable, Self.Body == Component {
  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    guard CKShouldCreateShellComponent() == false else {
      return SwiftReusableComponent(
        self,
        model: model
      )
    }

    // TODO: Reuse logic
    linkPropertyWrappersWithScopeHandle()

    defer {
      CKSwiftPopClass()
    }

    return SwiftReusableComponent(
      self,
      body: body,
      model: model
    )
  }
}

extension View where Self: ViewConfigurationRepresentable, Self.Body == Component {
  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    // TODO: Reuse logic
    let hasScopeHandle = linkPropertyWrappersWithScopeHandle(
      forceRequireNode: model?.isEmpty == false
    )

    defer {
      if hasScopeHandle {
        CKSwiftPopClass()
      }
    }

    return SwiftComponent(
      self,
      viewConfiguration: viewConfiguration,
      model: model
    )
  }
}

extension View where Self: ViewIdentifiable & ViewConfigurationRepresentable, Self.Body == Component {
  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    // TODO: Reuse logic
    linkPropertyWrappersWithScopeHandle()

    defer {
      CKSwiftPopClass()
    }

    return SwiftComponent(
      self,
      body: CKShouldCreateShellComponent() ? nil : body,
      viewConfiguration: viewConfiguration,
      model: model
    )
  }
}

extension View where Self: ViewIdentifiable & ViewConfigurationRepresentable & Equatable, Self.Body == Component {
  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    // TODO: Reuse logic
    linkPropertyWrappersWithScopeHandle()

    defer {
      CKSwiftPopClass()
    }

    return SwiftReusableComponent(
      self,
      body: CKShouldCreateShellComponent() ? nil : body,
      viewConfiguration: viewConfiguration,
      model: model
    )
  }
}

// MARK: Leaf component

extension View where Self: ViewConfigurationRepresentable, Self.Body == Never {
  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    // TODO: Reuse logic
    let hasScopeHandle = linkPropertyWrappersWithScopeHandle(
      forceRequireNode: model?.isEmpty == false)

    defer {
      if hasScopeHandle {
        CKSwiftPopClass()
      }
    }

    return SwiftComponent(
      self,
      viewConfiguration: viewConfiguration,
      model: model
    )
  }
}

extension View where Self: ViewConfigurationRepresentable & ViewIdentifiable, Self.Body == Never {
  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    // TODO: Reuse logic
    linkPropertyWrappersWithScopeHandle()

    defer {
      CKSwiftPopClass()
    }

    return SwiftComponent(
      self,
      viewConfiguration: viewConfiguration,
      model: model
    )
  }
}

extension View where Self: ViewConfigurationRepresentable & ViewIdentifiable & Equatable, Self.Body == Never {
  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    // TODO: Reuse logic
    linkPropertyWrappersWithScopeHandle()

    defer {
      CKSwiftPopClass()
    }

    return SwiftReusableLeafComponent(
      self,
      viewConfiguration: viewConfiguration,
      model: model
    )
  }
}

// MARK: Link

private extension View {

  private var linkableItems: [TreeNodeLinkable] {
    Mirror(reflecting: self)
      .children
      .compactMap {
        $0.value as? TreeNodeLinkable
      }
  }

  private func link(linkableItems: [TreeNodeLinkable], id: Any?) {
    let node = CKSwiftCreateNode(SwiftComponent<Self>.self, id)
    linkableItems
      .enumerated()
      .forEach { index, item in
        item.link(with: node, at: index)
      }
  }

  func linkPropertyWrappersWithScopeHandle(forceRequireNode: Bool) -> Bool {
    let linkableItems = self.linkableItems
    guard linkableItems.isEmpty == false || forceRequireNode || self is TreeNodeLinkableView else {
      return false
    }

    link(linkableItems: linkableItems, id: nil)
    return true
  }
}

extension View where Self: ViewIdentifiable {
  func linkPropertyWrappersWithScopeHandle() {
    link(linkableItems: linkableItems, id: id)
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import ComponentKit

public struct ViewAnimationModifier<Inflatable : ComponentInflatable> : ComponentInflatable {
  enum Directive {
    case initialMount(CAAnimation?)
    case finalUnmount(CAAnimation?)

    fileprivate func update(model: inout SwiftComponentModel.Animations) {
      switch self {
      case let .initialMount(animation?):
        model.initialMount.append(animation)
      case let .finalUnmount(animation?):
        model.finalUnmount.append(animation)
      default:
        break
      }
    }
  }

  let inflatable: Inflatable
  let directive: Directive

  // MARK: ComponentInflatable

  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    var model = model ?? SwiftComponentModel()
    directive.update(model: &model.animations)
    return inflatable.inflateComponent(with: model)
  }
}

extension ComponentInflatable {
  public func onInitialMount(_ animation: CAAnimation?) -> ViewAnimationModifier<Self> {
    ViewAnimationModifier(inflatable: self, directive: .initialMount(animation))
  }

  public func onFinalUnmount(_ animation: CAAnimation?) -> ViewAnimationModifier<Self> {
    ViewAnimationModifier(inflatable: self, directive: .finalUnmount(animation))
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import ComponentKit
import UIKit

private extension KeyPath where Root: NSObject {
  var asString: String {
    return NSExpression(forKeyPath: self).keyPath
  }
}

/// Represents the class of a view and the attributes that should be applied to it.
public struct ViewConfiguration {
  /// Represents a view configuration attribute.
  public struct Attribute<View: UIView> {
    /// The Objective-C bridgeable type.
    let componentViewAttribute: ComponentViewAttributeSwiftBridge

    /// Creates a new attribute.
    /// - Parameters:
    ///   - keyPath: The keypath where the value should be stored.
    ///   - value: The value to store.
    public init<Value>(_ keyPath: ReferenceWritableKeyPath<View, Value>, _ value: Value) {
      componentViewAttribute = ComponentViewAttributeSwiftBridge(identifier: keyPath.asString) { v in
        let view = v as! View
        view[keyPath: keyPath] = value
      }
    }

    /// Creates a new attribute
    /// - Parameter componentViewAttribute: The bridged view attribute.
    init(componentViewAttribute: ComponentViewAttributeSwiftBridge) {
      self.componentViewAttribute = componentViewAttribute
    }
  }

  /// Represents a view configuration layer attribute.
  public struct LayerAttribute {
    /// The Objective-C bridgeable type.
    let componentViewAttribute: ComponentViewAttributeSwiftBridge

    /// Creates a new layer attribute.
    /// - Parameters:
    ///   - keyPath: The keypath where the value should be stored.
    ///   - value: The value to store.
    public init<Value>(_ keyPath: ReferenceWritableKeyPath<CALayer, Value>, _ value: Value) {
      componentViewAttribute = ComponentViewAttributeSwiftBridge(identifier: "layer" + keyPath.asString) { view in
        view.layer[keyPath: keyPath] = value
      }
    }
  }

  /// The Objective-C bridgeable type.
  public let viewConfiguration: ComponentViewConfigurationSwiftBridge

  /// Creates a new configuration.
  /// - Parameters:
  ///   - viewClass: The view class to use.
  ///   - attributes: The view attributes.
  ///   - layerAttributes: The layer attribues.
  public init<View: UIView>(viewClass: View.Type, attributes: [Attribute<View>] = [], layerAttributes: [LayerAttribute] = []) {
    viewConfiguration = ComponentViewConfigurationSwiftBridge(viewClass: viewClass,
                                                              attributes: attributes.map { $0.componentViewAttribute } + layerAttributes.map { $0.componentViewAttribute })
  }

#if swift(>=5.1)
  /// Creates a new configuration.
  /// - Parameters:
  ///   - viewClass: The view class to use.
  ///   - attributes: The view/layer attributes.
  public init<View: UIView>(viewClass: View.Type, @ViewConfigurationAttributeBuilder<View> attributes: () -> [ComponentViewAttributeSwiftBridge]) {
    viewConfiguration = ComponentViewConfigurationSwiftBridge(viewClass: viewClass, attributes: attributes())
  }
#endif
}

#if swift(>=5.1)
@_functionBuilder
public struct ViewConfigurationAttributeBuilder<View: UIView> {
  public enum Directive {
    case attribute(ViewConfiguration.Attribute<View>)
    case layerAttribute(ViewConfiguration.LayerAttribute)

    fileprivate var swiftBridge: ComponentViewAttributeSwiftBridge {
      switch self {
      case let .attribute(attribute):
        return attribute.componentViewAttribute
      case let .layerAttribute(attribute):
        return attribute.componentViewAttribute
      }
    }
  }

  #if swift(<5.3)
  public static func buildBlock(_ parts: Directive...) -> [ComponentViewAttributeSwiftBridge] {
    parts.map { $0.swiftBridge }
  }
  #endif

  public static func buildBlock(_ parts: Directive...) -> [Directive] {
    parts
  }

  public static func buildFinalResult(_ parts: [Directive]) -> [Directive] {
    parts
  }

  public static func buildFinalResult(_ parts: [Directive]) -> [ComponentViewAttributeSwiftBridge] {
    parts.map { $0.swiftBridge }
  }

  public static func buildExpression(_ attr: ViewConfiguration.Attribute<View>) -> Directive {
    .attribute(attr)
  }

  public static func buildExpression(_ attr: ViewConfiguration.LayerAttribute) -> Directive {
    .layerAttribute(attr)
  }

  public static func buildExpression<Value>(_ attr: (key: ReferenceWritableKeyPath<View, Value>, value: Value)) -> Directive {
    .attribute(ViewConfiguration.Attribute<View>(attr.key, attr.value))
  }

  public static func buildExpression<Value>(_ attr: (key: ReferenceWritableKeyPath<CALayer, Value>, value: Value)) -> Directive {
    .layerAttribute(ViewConfiguration.LayerAttribute(attr.key, attr.value))
  }
}

#endif


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit
import UIKit

// MARK: ViewConfigurationRepresentable

/// Can be represented with a `ViewConfiguration`.  By contract
/// the `ViewConfiguration` was built with attributes matching its view.
public protocol ViewConfigurationRepresentable {
  var viewConfiguration: ViewConfiguration { get }
}

/// Can be represented with `Attribute` / `LayerAttribute`.
public protocol ViewAttributeRepresentable : ViewConfigurationRepresentable {
  associatedtype UIViewType: UIView

  var attributes: [ViewConfiguration.Attribute<UIViewType>] { get }
  var layerAttributes: [ViewConfiguration.LayerAttribute] { get }
}

extension ViewAttributeRepresentable {
  public var viewConfiguration: ViewConfiguration {
    ViewConfiguration(
      viewClass: UIViewType.self,
      attributes: attributes,
      layerAttributes: layerAttributes
    )
  }
}

/// Can be represented and externally defined by `Attribute` / `LayerAttribute`. Use
/// for builder style extension below.
public protocol ViewAttributeAssignable : ViewAttributeRepresentable  {
  var attributes: [ViewConfiguration.Attribute<UIViewType>] { get set }
  var layerAttributes: [ViewConfiguration.LayerAttribute] { get set }
}

extension ViewAttributeAssignable where Self: ComponentInflatable {

  public func attributes<Value>(_ keyPath: ReferenceWritableKeyPath<UIViewType, Value>, _ value: Value) -> Self {
    var copy = self
    copy.attributes.append(ViewConfiguration.Attribute(keyPath, value))
    return copy
  }

  public func attribute<Value>(_ layerAttribute: ReferenceWritableKeyPath<CALayer, Value>, _ value: Value) -> Self {
    var copy = self
    copy.layerAttributes.append(ViewConfiguration.LayerAttribute(layerAttribute, value))
    return copy
  }

  // MARK: Attributes

  public func backgroundColor(_ value: UIColor?) -> Self {
    attributes(\.backgroundColor, value)
  }

  public func alpha(_ value: CGFloat) -> Self {
    attributes(\.alpha, value)
  }

  public func clipsToBounds(_ value: Bool) -> Self {
    attributes(\.clipsToBounds, value)
  }

  public func userInteractionEnabled(_ value: Bool) -> Self {
    attributes(\.isUserInteractionEnabled, value)
  }

  public func contentMode(_ value: UIView.ContentMode) -> Self {
    attributes(\.contentMode, value)
  }

  // MARK: Layer Attributes

  public func borderWidth(_ value: CGFloat) -> Self {
    attribute(\.borderWidth, value)
  }

  public func borderColor(_ value: CGColor?) -> Self {
    attribute(\.borderColor, value)
  }

  public func borderColor(_ value: UIColor?) -> Self {
    borderColor(value?.cgColor)
  }

  public func cornerRadius(_ value: CGFloat) -> Self {
    attribute(\.cornerRadius, value)
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit

typealias ViewLayoutCenteringOptions = CenterLayoutComponent.CenteringOptions
typealias ViewLayoutCenteringSizingOptions = CenterLayoutComponent.SizingOptions

public struct ViewLayoutModifier<Inflatable: ComponentInflatable> : ComponentInflatable {
  enum Directive {
    case frame(ComponentSize)
    case padding(top: Dimension, left: Dimension, bottom: Dimension, right: Dimension)
    case ratio(CGFloat)
    case center(centeringOptions: ViewLayoutCenteringOptions, sizingOptions: ViewLayoutCenteringSizingOptions)
    case background(() -> Component)
    case overlay(() -> Component)

    fileprivate func build(_ content: Component) -> Component {
      switch self {
      case let .frame(size):
        return SizingComponent(swiftSize: size.componentSize, component: content)
      case let .padding(top, left, bottom, right):
        return InsetComponent(swiftView: nil,
                              top: top.dimension,
                              left: left.dimension,
                              bottom: bottom.dimension,
                              right: right.dimension,
                              component: content)
      case let .ratio(ratio):
        return RatioLayoutComponent(ratio: ratio, swiftSize: nil, component: content)
      case let .center(centeringOptions, sizingOptions):
        return CenterLayoutComponent(centeringOptions: centeringOptions,
                                     sizingOptions: sizingOptions,
                                     child: content,
                                     swiftSize: nil)
      case let .background(background):
        return BackgroundLayoutComponent(component: content, background: background())
      case let .overlay(overlay):
        return OverlayLayoutComponent(component: content, overlay: overlay())
      }
    }
  }

  let inflatable: Inflatable
  let directive: Directive

  // MARK: ComponentInflatable

  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    let inflatedTarget = inflatable.inflateComponent(with: nil)
    return directive.build(inflatedTarget)
      .inflateComponent(with: model)
  }
}

extension ComponentInflatable {

  public func frame(width: CGFloat? = nil,
                    height: CGFloat? = nil,
                    minWidth: CGFloat? = nil,
                    minHeight: CGFloat? = nil,
                    maxWidth: CGFloat? = nil,
                    maxHeight: CGFloat? = nil) -> ViewLayoutModifier<Self> {
    ViewLayoutModifier(
      inflatable: self,
      directive: .frame(
        ComponentSize(
          width: width.map { .points($0) },
          height: height.map { .points($0) },
          minWidth: minWidth.map { .points($0) },
          minHeight: minHeight.map { .points($0) },
          maxWidth: maxWidth.map { .points($0) },
          maxHeight: maxHeight.map { .points($0) })
      )
    )
  }

  public func relativeFrame(width: CGFloat? = nil,
                            height: CGFloat? = nil,
                            minWidth: CGFloat? = nil,
                            minHeight: CGFloat? = nil,
                            maxWidth: CGFloat? = nil,
                            maxHeight: CGFloat? = nil) -> ViewLayoutModifier<Self> {
    ViewLayoutModifier(
      inflatable: self,
      directive: .frame(
        ComponentSize(
          width: width.map { .percent($0) },
          height: height.map { .percent($0) },
          minWidth: minWidth.map { .percent($0) },
          minHeight: minHeight.map { .percent($0) },
          maxWidth: maxWidth.map { .percent($0) },
          maxHeight: maxHeight.map { .percent($0) })
      )
    )
  }

  public func overlay(@ComponentBuilder _ overlay: @escaping () -> Component) -> ViewLayoutModifier<Self> {
    ViewLayoutModifier(inflatable: self, directive: .overlay(overlay))
  }

  public func background(@ComponentBuilder _ background: @escaping () -> Component) -> ViewLayoutModifier<Self> {
    ViewLayoutModifier(inflatable: self, directive: .background(background))
  }

  // TODO: CenterLayoutComponent options shouldn't leak
  public func center(centeringOptions: CenterLayoutComponent.CenteringOptions = [],
                     sizingOptions: CenterLayoutComponent.SizingOptions = []) -> ViewLayoutModifier<Self> {
    ViewLayoutModifier(inflatable: self, directive: .center(centeringOptions: centeringOptions, sizingOptions: sizingOptions))
  }

  public func ratio(_ ratio: CGFloat) -> ViewLayoutModifier<Self> {
    ViewLayoutModifier(inflatable: self, directive: .ratio(ratio))
  }

  public func padding(top: CGFloat = 0,
                      left: CGFloat = 0,
                      bottom: CGFloat = 0,
                      right: CGFloat = 0) -> ViewLayoutModifier<Self> {
    ViewLayoutModifier(
      inflatable: self,
      directive: .padding(
        top: .points(top),
        left: .points(left),
        bottom: .points(bottom),
        right: .points(right)
      )
    )
  }

  public func padding(_ insets: UIEdgeInsets) -> ViewLayoutModifier<Self> {
    padding(top: insets.top, left: insets.left, bottom: insets.bottom, right: insets.right)
  }

  public func padding(_ length: CGFloat = 0) -> ViewLayoutModifier<Self> {
    padding(top: length, left: length, bottom: length, right: length)
  }

  public func relativePadding(top: CGFloat = 0,
                              left: CGFloat = 0,
                              bottom: CGFloat = 0,
                              right: CGFloat = 0) -> ViewLayoutModifier<Self> {
    ViewLayoutModifier(
      inflatable: self,
      directive: .padding(
        top: .percent(top),
        left: .percent(left),
        bottom: .percent(bottom),
        right: .percent(right)
      )
    )
  }

  public func relativePadding(_ length: CGFloat = 0) -> ViewLayoutModifier<Self> {
    relativePadding(top: length, left: length, bottom: length, right: length)
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import ComponentKit

public struct ViewLifecycleModifier<Inflatable : ComponentInflatable> : ComponentInflatable {
  enum Directive {
    case didInit(()  -> Void)
    case willMount(()  -> Void)
    case didUnmount(()  -> Void)
    case willDispose(()  -> Void)

    fileprivate func update(model: inout SwiftComponentModel.LifecycleCallbacks) {
      switch self {
      case let .didInit(callback):
        model.didInit.append(callback)
      case let .willMount(callback):
        model.willMount.append(callback)
      case let .didUnmount(callback):
        model.didUnmount.append(callback)
      case let .willDispose(callback):
        model.willDispose.append(callback)
      }
    }
  }

  let inflatable: Inflatable
  let directive: Directive

  // MARK: ComponentInflatable

  public func inflateComponent(with model: SwiftComponentModel?) -> Component {
    var model = model ?? SwiftComponentModel()
    directive.update(model: &model.lifecycleCallbacks)
    return inflatable.inflateComponent(with: model)
  }
}

extension ComponentInflatable {
  public func onDidInit(_ callback: @escaping () -> Void) -> ViewLifecycleModifier<Self> {
    ViewLifecycleModifier(inflatable: self, directive: .didInit(callback))
  }

  public func onWillMount(_ callback: @escaping () -> Void) -> ViewLifecycleModifier<Self> {
    ViewLifecycleModifier(inflatable: self, directive: .willMount(callback))
  }

  public func onDidUnmount(_ callback: @escaping () -> Void) -> ViewLifecycleModifier<Self> {
    ViewLifecycleModifier(inflatable: self, directive: .didUnmount(callback))
  }

  public func onWillDispose(_ callback: @escaping () -> Void) -> ViewLifecycleModifier<Self> {
    ViewLifecycleModifier(inflatable: self, directive: .willDispose(callback))
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit

protocol ScopeHandleAssignable : class {
  func assign(handle: CKComponentScopeHandle)
}

@propertyWrapper
public struct ViewModel<Value: AnyObject> : TreeNodeLinkable {
  private let store: TreeNodeValueStore<Value>

  public init(wrappedValue valueProvider: @escaping @autoclosure () -> Value) {
    self.store = TreeNodeValueStore(valueProvider: valueProvider)
  }

  /// Should only be called during component build or on the main thread thereafter
  public var wrappedValue: Value {
    store.get()
  }

  // MARK: ScopeHandleLinkable

  func link(with node: CKTreeNode, at index: Int) {
    if store.link(with: node, at: index) {
      Mirror(reflecting: wrappedValue)
        .children
        .compactMap {
          $0.value as? ScopeHandleAssignable
        }
        .forEach {
          $0.assign(handle: node.scopeHandle)
        }
    }
  }
}

extension ViewModel : Equatable {
  static public func ==(lhs: ViewModel, rhs: ViewModel) -> Bool {
    lhs.wrappedValue === rhs.wrappedValue
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import Foundation
import ComponentKit

@propertyWrapper
public class ViewModelState<Value> : ScopeHandleAssignable {
  private var handle: CKComponentScopeHandle?

  public init(wrappedValue valueProvider: @escaping @autoclosure () -> Value) {
    self.wrappedValue = valueProvider()
  }

  /// Should only be called during component build or on the main thread thereafter
  public var wrappedValue: Value {
    didSet {
      // Permit changing the view model state's value before it is linked.
      // which would be the case if the view model state is re-assigned before
      // its owning view has rendered (and thus read the values).
      handle.flatMap(CKSwiftUpdateViewModelState)
    }
  }

  public var projectedValue: Binding<Value> {
    Binding(get: { self.wrappedValue }, set: { self.wrappedValue = $0 })
  }

  // MARK: ScopeHandleAssignable

  func assign(handle: CKComponentScopeHandle) {
    // Storing the handle would result in a retain cycle (@ViewModelState -> @ViewModel -> Component ->
    // State which it turn is owned by CKComponentScopeHandle). Keeping a weak reference would also not work since
    // after a few generations the the scope handle wouldn't be owned by anyone else. @ViewModelState only gets
    // assigned the first time around as it's only created once. As soon as D21332674 lands we can refactor this.
    self.handle = handle.newStateless()
  }
}

extension ViewModelState : Equatable where Value : Equatable {
  static public func ==(lhs: ViewModelState, rhs: ViewModelState) -> Bool {
    lhs.wrappedValue == rhs.wrappedValue
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import XCTest
import CKSwift

class ActionTests : XCTestCase {
  var param: Int?

  private func actionHandler(param: Int) {
    self.param = param
  }

  override func setUp() {
    super.setUp()
    param = nil
  }

  func test_TargetHandlerAction_Works() {
    let action = ActionWith<Int>(target: self, handler: type(of: self).actionHandler)

    action.invoke(42)

    XCTAssertEqual(param, 42)
  }

  func test_TargetHandlerAction_DoesNotRetain() {
    class Handler { func doNothing() { }}

    weak var weakHandler: Handler?
    var action: Action?

    autoreleasepool {
      let handler = Handler()
      weakHandler = handler
      action = Action(target: handler, handler: type(of: handler).doNothing)
      XCTAssertNotNil(weakHandler)
      XCTAssertNotNil(action)
    }
    XCTAssertNil(weakHandler)
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import XCTest
import CKSwift

class CKSwiftTests : XCTestCase {
  func test_InitialisingDimension() {
    XCTAssertEqual(Dimension.points(20).description, "20pt")
    XCTAssertEqual(Dimension.percent(0.5).description, "50%")
  }

  func test_WhenInitialisedWithFloatLiteral_InitialisesDimensionWithPoints() {
    let d: CKSwift.Dimension = 42.0
    XCTAssertEqual(d.description, "42pt")
  }

  func test_WhenInitialisedWithIntLiteral_InitialisesDimensionWithPoints() {
    let d: CKSwift.Dimension = 42
    XCTAssertEqual(d.description, "42pt")
  }

  func test_InitialisingComponentSizeWithCGSize() {
    let expected = "<RCComponentSize: exact={100pt, 200pt}, min={Auto, Auto}, max={Auto, Auto}>"
    XCTAssertEqual(ComponentSize(size: CGSize(width: 100, height: 200)).description, expected)
  }

  func test_InitialisingComponentSizeWithDimensions() {
    let expected = "<RCComponentSize: exact={200pt, 300pt}, min={100pt, 50pt}, max={400pt, 600pt}>"
    XCTAssertEqual(ComponentSize(width: 200, height: 300, minWidth: 100, minHeight: 50, maxWidth: 400, maxHeight: 600).description, expected)
  }

  func test_WhenDimensionIsOmitted_ThisDimensionInitialisedToAuto() {
    let expected = "<RCComponentSize: exact={50%, Auto}, min={Auto, Auto}, max={Auto, Auto}>"
    XCTAssertEqual(ComponentSize(width: .percent(0.5)).description, expected)
  }

  func test_InitialisingSizeRange() {
    let sizeRange = SizeRange(minSize: CGSize(width: 100, height: 200), maxSize: CGSize(width: 200, height: 300))

    let expected = "<CKSizeRange: min={100, 200}, max={200, 300}>"
    XCTAssertEqual(sizeRange.description, expected)
  }

  func test_WhenSizingHostingView_InvokesSizeRangeProvider() {
    var sizeRangeProviderWasCalledWithExpectedSize = false
    let expectedSize = CGSize(width: 320, height: 480)
    let hv = ComponentHostingView<NSNumber, NSObject>(
      componentProvider: { _, _ in return Component() },
      sizeRangeProviderBlock: { size in
        sizeRangeProviderWasCalledWithExpectedSize = (size == expectedSize)
        return SizeRange(minSize: size, maxSize: size)
    })
    hv.updateModel(2, mode: .asynchronous)
    hv.updateContext(nil, mode: .synchronous)

    let _ = hv.sizeThatFits(expectedSize)

    XCTAssert(sizeRangeProviderWasCalledWithExpectedSize)
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import XCTest
import CKSwift

private func makeChild() -> FlexboxComponent.Child {
  fatalError()
}

private func buildComponents(@FlexboxChildrenBuilder children: () -> [FlexboxComponent.Child]) { }

private struct MyView: CKSwift.View, ViewConfigurationRepresentable {
  let viewConfiguration = ViewConfiguration(viewClass: UIView.self)
}

class FlexboxChildrenBuilderTests : XCTestCase {

  func testConvertSingleComponentToChild() {
    buildComponents {
      Component()
    }
  }

  func testConvertSingleViewToChild() {
    buildComponents {
      ComponentView<UIView>()
    }
  }

  func testSingleChildWithSingleComponent() {
    buildComponents {
      makeChild()
    }
  }

  func testMulti() {
    buildComponents {
      ComponentView<UIView>()
      Component()
      makeChild()
      ComponentView<UIView>()
      Component()
      makeChild()
    }
  }

  func testConditions() {
    let condition = Int.random(in: 0..<100) == 0
    buildComponents {
      if condition {
        Component()
      }

      if condition {
        ComponentView<UIView>()
      }

      if condition {
        makeChild()
      }

      if condition {
        Component()
      } else {
        ComponentView<UIView>()
      }

      if condition {
        ComponentView<UIView>()
      } else {
        Component()
      }

      if condition {
        ComponentView<UIView>()
      } else {
        Component()
      }

      if condition {
        makeChild()
      } else {
        makeChild()
      }
    }
  }

  func testSwitch() {
    buildComponents {
      switch Int.random(in: 0..<3) {
      case 0:
        Component()
      case 1:
        makeChild()
      default:
        ComponentView<UIView>()
      }
    }
  }

  func testInputArray() {
    let children: [FlexboxComponent.Child] = []
    let children2: Set<FlexboxComponent.Child> = []
    let childrenComponents: [Component] = []
    let childrenViews: [MyView] = []
    buildComponents {
      children
      children2
      childrenComponents
      childrenViews
    }
  }

  func testLimitedAvailability() {
    buildComponents {
      if #available(macOS 9, *) {
        ComponentView<UIView>()
      }

      if #available(macOS 10.16, *) {
        ComponentView<UIView>()
      }

      if #available(macOS 9000, *) {
        ComponentView<UIView>()
      }
    }
  }

  func testDo() {
    buildComponents {
      do {
        Component()
        ComponentView<UIView>()
        makeChild()
      }
    }
  }
}


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import UIKit
import ComponentKit
import ComponentTextKit

// Re-export the type to avoid importing ComponentTextKit directly and ambiguities in type lookup
public typealias LabelComponent = ComponentTextKit.LabelComponent


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

import ComponentKit
import CKSwift
import UIKit

public extension LabelComponent {

  #if swift(>=5.1)
  convenience init(
    text: String,
    truncationString: String? = nil,
    font: UIFont? = nil,
    color: UIColor? = nil,
    lineBreakMode: NSLineBreakMode = .byWordWrapping,
    maximumNumberOfLines: UInt = 0,
    shadowOffset: CGSize = .zero,
    shadowColor: UIColor? = nil,
    shadowOpacity: CGFloat = 0,
    shadowRadius: CGFloat = 0,
    alignment: NSTextAlignment = .natural,
    firstLineHeadIndent: CGFloat = 0,
    headIndent: CGFloat = 0,
    tailIndent: CGFloat = 0,
    lineHeightMultiple: CGFloat = 0,
    maximumLineHeight: CGFloat = 0,
    minimumLineHeight: CGFloat = 0,
    lineSpacing: CGFloat = 0,
    paragraphSpacing: CGFloat = 0,
    paragraphSpacingBefore: CGFloat = 0,
    size: ComponentSize? = nil,
    @ViewConfigurationAttributeBuilder<UIView> attributes: () -> [ComponentViewAttributeSwiftBridge] = {[]}) {
    self.init(__text: text,
              truncationString: truncationString,
              font: font,
              color: color,
              lineBreakMode: lineBreakMode,
              maximumNumberOfLines: maximumNumberOfLines,
              shadowOffset: shadowOffset,
              shadowColor: shadowColor,
              shadowOpacity: shadowOpacity,
              shadowRadius: shadowRadius,
              alignment: alignment,
              firstLineHeadIndent: firstLineHeadIndent,
              headIndent: headIndent,
              tailIndent: tailIndent,
              lineHeightMultiple: lineHeightMultiple,
              maximumLineHeight: maximumLineHeight,
              minimumLineHeight: minimumLineHeight,
              lineSpacing: lineSpacing,
              paragraphSpacing: paragraphSpacing,
              paragraphSpacingBefore: paragraphSpacingBefore,
              swiftSize: size?.componentSize,
              swiftAttributes: attributes())
  }
#endif
}


/* This file provided by Facebook is for non-commercial testing and evaluation
 * purposes only.  Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

import Foundation
import UIKit
import CKSwift
import CKTextSwift

#if swift(>=5.2)

public struct Quote : Equatable {
  public enum Style : Int, CaseIterable {
    case frosted
    case monochrome
    case sombre
    case warm

    public init(value: Int) {
      self = Style(rawValue: value % Style.allCases.count)!
    }

    public var name: String {
      switch self {
      case .frosted:
        return "Frosted"
      case .monochrome:
        return "Monochrome"
      case .sombre:
        return "Sombre"
      case .warm:
        return "Warm"
      }
    }
  }

  public init(id: UUID, text: String, author: String, style: Style) {
    self.id = id
    self.text = text
    self.author = author
    self.style = style
  }

  public init(id: UUID, text: String, author: String, styleValue: Int) {
    self.id = id
    self.text = text
    self.author = author
    self.style = Style(value: styleValue)
  }

  public let id: UUID
  public let text: String
  public let author: String
  public let style: Style
}

public class QuoteStore {
  public let quotes: [Quote]
  public static let shared = QuoteStore()

  init() {
    self.quotes = QuoteStore.data
      .enumerated()
      .map {
        Quote(id: UUID(), text: $0.element["text"]!, author: $0.element["author"]!, style: Quote.Style(value: $0.offset))
      }
  }

  func quote(at index: Int) -> Quote {
    quotes[index]
  }

  private static let data = [
    [
      "text": "I have the simplest tastes. I am always satisfied with the best.",
      "author": "Oscar Wilde"
    ], [
      "text": "A thing is not necessarily true because a man dies for it.",
      "author": "Oscar Wilde"
    ], [
      "text": "A poet can survive everything but a misprint.",
      "author": "Oscar Wilde"
    ], [
      "text": "He is really not so ugly after all, provided, of course, that one shuts one's eyes, and does not look at him.",
      "author": "Oscar Wilde"
    ], [
      "text": "People who count their chickens before they are hatched act very wisely because chickens run about so absurdly that it's impossible to count them accurately.",
      "author": "Oscar Wilde"
    ], [
      "text": "It is better to have a permanent income than to be fascinating.",
      "author": "Oscar Wilde"
    ], [
      "text": "Education is an admirable thing. But it is well to remember from time to time that nothing that is worth knowing can be taught.",
      "author": "Oscar Wilde"
    ], [
      "text": "Art is the only serious thing in the world. And the artist is the only person who is never serious.",
      "author": "Oscar Wilde"
    ], [
      "text": "A man who does not think for himself does not think at all.",
      "author": "Oscar Wilde"
    ], [
      "text": "Prayer must never be answered: if it is, it ceases to be prayer and becomes correspondence.",
      "author": "Oscar Wilde"
    ], [
      "text": "The philosophers have only interpreted the world, in various ways. The point, however, is to change it.",
      "author": "Anonymous"
    ], [
      "text": "To improve is to change, so to be perfect is to have changed often.",
      "author": "Anonymous"
    ], [
      "text": "False words are not only evil in themselves, but they infect the soul with evil.",
      "author": "Anonymous"
    ], [
      "text": "It has been said that love robs those who have it of their wit, and gives it to those who have none.",
      "author": "Anonymous"
    ], [
      "text": "The greatest honor history can bestow is the title of peacemaker.",
      "author": "Anonymous"
    ], [
      "text": "The guest who has escaped from the roof, will think twice before he comes back in by the door.",
      "author": "Anonymous"
    ], [
      "text": "I'll not assert that it was a diversion which prevented a war, but nevertheless, it was a diversion.",
      "author": "Anonymous"
    ], [
      "text": "I gyve unto my wief my second best bed with the furniture.",
      "author": "Anonymous"
    ], [
      "text": "He who knows when he can fight and when he cannot will be victorious.",
      "author": "Anonymous"
    ], [
      "text": "Sticks and stones may break my bones but words will never hurt me.",
      "author": "Anonymous"
    ], [
      "text": "It'll be boring when it's not fun any more.",
      "author": "Anonymous"
    ]
  ]
}

public struct QuoteContext {
  private let images: [String: UIImage]

  init() {
    self.images = Dictionary(uniqueKeysWithValues: [
      "LosAngeles",
      "MarketStreet",
      "Drops",
      "Powell"
    ].map {
      ($0, UIImage(named: $0)!)
    })
  }

  func image(named name: String) -> UIImage {
    images[name]!
  }

  public static let shared = QuoteContext()
}

#endif


/* This file provided by Facebook is for non-commercial testing and evaluation
 * purposes only.  Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

import Foundation
import UIKit
import CKSwift
import CKTextSwift

extension CAAnimation {
  static let fadeId: CAAnimation = {
    let animation = CABasicAnimation(keyPath: "transform")
    animation.fromValue = NSValue(caTransform3D: CATransform3DMakeScale(0.0, 0.0, 0.0))
    animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
    animation.duration = 0.2
    return animation
  }()
}

struct SwiftInteractiveQuoteView : View, ViewIdentifiable, ViewConfigurationRepresentable, Actionable {
  let quote: Quote
  let context: QuoteContext
  @Binding var revealAnswer: Bool

  var body: Component {
    FlexboxComponent(
      view: ViewConfiguration(viewClass: UIView.self) {
        onTap { view, _ in
          view.revealAnswer.toggle()
        }
      },
      alignItems: .stretch) {
      QuoteView(quote: quote, context: context)
        .overlay {
          if revealAnswer {
            SuccessIndicatorView(
              successful:quote.author == "Oscar Wilde",
              successText: "This quote is by Oscar Wilde",
              failureText: "This quote isn't by Oscar Wilde"
            )
            .onInitialMount(.fadeId)
          }
        }
      ComponentView<UIView> {
        (\.backgroundColor, .lightGray)
      }
      .frame(height: 1 / UIScreen.main.scale)
    }
  }

  let id = "Toto"
  let viewConfiguration = ViewConfiguration(viewClass: UIView.self)
}

fileprivate struct SuccessIndicatorView : View {
  let successful: Bool
  let successText: String
  let failureText: String

  var body: Component {
    WrapperComponentView {
      (\.backgroundColor, .clear)
      (\.isUserInteractionEnabled, false)
    } component: {
      FlexboxComponent(alignItems: .center) {
        LabelComponent(
          text: successful ? "Yes" : "No",
          font: UIFont(name: "Cochin-Bold", size: 45),
          color: .white,
          alignment: .center) {
            (\.backgroundColor, .clear)
        }
        LabelComponent(
          text: successful ? successText : failureText,
          font: UIFont(name: "Cochin", size: 20),
          color: .white,
          alignment: .center) {
            (\.backgroundColor, .clear)
        }
      }
    }
    .padding(top: 40, left: 20, bottom: 40, right: 20)
    .background {
      let color = successful ?
        UIColor(red: 0.1, green: 0.4, blue: 0.1, alpha: 0.9)
        : UIColor(red: 0.7, green: 0.1, blue: 0.1, alpha: 0.9)
      ComponentView<UIView> {
        (\.backgroundColor, color)
        (\.cornerRadius, 10)
      }
    }
    .center(centeringOptions: .Y, sizingOptions: .minimumY)
    .padding(left: 20, right: 20)
  }
}

final class InteractiveQuoteViewModel {
  @ViewModelState var revealAnswer = false
}

public struct SwiftInteractiveWrapperQuoteView : View, ViewIdentifiable, Equatable {
  let quote: Quote
  let context: QuoteContext

  @ViewModel var viewModel = InteractiveQuoteViewModel()

  public init(quote: Quote, context: QuoteContext) {
    self.quote = quote
    self.context = context
  }

  public var body: Component {
    SwiftInteractiveQuoteView(
      quote: quote,
      context: context,
      revealAnswer: viewModel.$revealAnswer
    )
  }

  public var id: UUID {
    quote.id
  }

  public static func ==(lhs: SwiftInteractiveWrapperQuoteView, rhs: SwiftInteractiveWrapperQuoteView) -> Bool {
    return lhs.quote == rhs.quote
  }
}

fileprivate struct LabelView: View {
  let text: String
  let font: UIFont?
  let color: UIColor?

  init(text: String, font: UIFont? = nil, color: UIColor? = nil) {
    self.text = text
    self.font = font
    self.color = color
  }

  var body: Component {
    LabelComponent(
      text: text,
      font: font,
      color: color) {
        (\.backgroundColor, .clear)
        (\.isUserInteractionEnabled, false)
    }
  }
}

fileprivate struct QuoteView : View {
  let quote: Quote
  let context: QuoteContext

  var body: Component {
    switch quote.style {
    case .frosted:
      FrostedQuoteView(text: quote.text, context: context)
    case .monochrome:
      MonochromeQuoteView(text: quote.text, context: context)
    case .sombre:
      SombreQuoteView(text: quote.text, context: context)
    case .warm:
      WarmQuoteView(
        text: quote.text,
        context: context
      )
      .onWillMount {
        print("Will Mount")
      }
      .onWillMount {
        print("Will Mount 2")
      }
      .onWillMount {
        print("Will Mount 3")
      }
    }
  }
}

fileprivate struct SombreQuoteView : View {
  let text: String
  let context: QuoteContext

  var body: Component {
    let line = ComponentView<UIView> {
      (\.backgroundColor, .white)
    }
    .frame(width: 50, height: 5)

    QuoteWithBackgroundView(
      image: context.image(named: "MarketStreet")) {
      FlexboxComponent(
        spacing: 50,
        alignItems: .start) {
        line
        LabelView(
          text: text.uppercased(),
          font: UIFont(name: "Avenir-Black", size: 25),
          color: .white
        )
        line
      }
      .padding(top: 40, left: 30, bottom: 40, right: 30)
    }
  }
}

fileprivate struct QuoteWithBackgroundView : View {
  let image: UIImage
  let component: () -> Component

  init(image: UIImage, @ComponentBuilder component: @escaping () -> Component) {
    self.image = image
    self.component = component
  }

  var body: Component {
    component()
      .background {
        ComponentView<UIImageView> {
          (\.image, image)
          (\.contentMode, .scaleAspectFill)
          (\.clipsToBounds, true)
        }
      }
  }
}

fileprivate struct FrostedQuoteView : View  {
  let text: String
  let context: QuoteContext

  var body: Component {
    QuoteWithBackgroundView(image: context.image(named: "LosAngeles")) {
      FlexboxComponent(alignItems: .start) {
        FlexboxComponent.Child(alignSelf: .center) {
          LabelView(
            text: text,
            font: UIFont(name: "Baskerville", size: 30)
          )
        }
        FlexboxComponent.Child(alignSelf: .end) {
          LabelView(
            text: "\u{201D}",
            font: UIFont(name: "Baskerville", size: 140),
            color: UIColor.white.withAlphaComponent(0.5)
          )
          .padding(right: 5)
        }
      }
      .padding(top: 70, left: 20, bottom: 25, right: 20)
    }
  }
}

fileprivate struct MonochromeQuoteView : View  {
  let text: String
  let context: QuoteContext

  var body: Component {
    QuoteWithBackgroundView(image: context.image(named: "Drops")) {
      FlexboxComponent(
        direction: .row,
        alignItems: .start) {
        FlexboxComponent.Child(spacingBefore: 10) {
          ComponentView<UIView> {
            (\.backgroundColor, .darkGray)
          }
          .frame(width: 30, height: 40)
        }
        FlexboxComponent.Child(flexShrink: 1, flexBasis: .percent(1.0)) {
          LabelView(
            text: text,
            font: UIFont(name: "HoeflerText-Italic", size: 25),
            color: .darkGray
          )
          .padding(top: 50, left: 20, bottom: 50, right: 20)
        }
      }
      .background {
        ComponentView<UIView> {
          (\.backgroundColor, UIColor.white.withAlphaComponent(0.7))
        }
      }
      .padding(top: 40, bottom: 40)
    }
  }
}

fileprivate struct WarmQuoteView : View {
  let text: String
  let context: QuoteContext

  var body: Component {
    QuoteWithBackgroundView(image: context.image(named: "Powell")) {
      LabelView(
        text: text,
        font: UIFont(name: "AmericanTypewriter", size: 26)
      )
      .padding(top: .infinity, left: 30, bottom: .infinity, right: 30)
      .ratio(1.3)
    }
  }
}


/* This file provided by Facebook is for non-commercial testing and evaluation
 * purposes only.  Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

import Foundation
import CKSwift
import CKTextSwift

@ComponentBuilder public func SwiftQuoteComponent(from quote: Quote) -> Component {
  SwiftInteractiveWrapperQuoteView(quote: quote, context: QuoteContext.shared)
}

@objc public class Trampoline : NSObject {
  @objc public class func component(uuid: UUID, text: String, author: String, style: Int) -> Component {
    SwiftQuoteComponent(from: Quote(id: uuid, text: text, author: author, style: Quote.Style(value: style)))
  }
}


// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import PackageDescription
import Foundation

#if os(Linux)
import Glibc
#else
import Darwin.C
#endif

let package = Package(
    name: "Facebook",
    platforms: [.iOS(.v12)],
    products: [
        // The Kernel of the SDK. Must be included as a runtime dependency.
        .basics,

        // The Facebook AEM Kit
        .aem,

        /*
          The Core SDK library that provides two importable modules:

            - FacebookCore which includes the most current interface and
              will contain interfaces for new features written in Swift

            - FBSDKCoreKit which contains legacy Objective-C interfaces
              that will be used to maintain backwards compatibility with
              types that have been converted to Swift.
              This will not contain interfaces for new features written in Swift.
         */
        .core,

        // The Facebook Login SDK
        .login,

        // The Facebook Share SDK
        .share,

        // The Facebook Gaming Services SDK
        .gaming,
    ],
    targets: [
        // The kernel of the SDK
        .Prefixed.basics,

        /*
          The legacy Objective-C implementation that will be converted to Swift.
          This will not contain interfaces for new features written in Swift.
        */
        .Prefixed.aem,

        // The main AEM module
        .aem,

        /*
          The legacy Objective-C implementation that will be converted to Swift.
          This will not contain interfaces for new features written in Swift.
        */
        .Prefixed.core,

        // The main Core SDK module
        .core,

        /*
          The legacy Objective-C implementation that will be converted to Swift.
          This will not contain interfaces for new features written in Swift.
        */
        .Prefixed.login,

        // The main Login SDK module
        .login,

        /*
          The legacy Objective-C implementation that has been converted to Swift.
          This will not contain interfaces for new features written in Swift.
        */
        .Prefixed.share,

        // The main Share SDK module
        .share,

        /*
          The legacy Objective-C implementation that has been converted to Swift.
          This will not contain interfaces for new features written in Swift.
        */
        .Prefixed.gamingServices,

        // The main Facebook Gaming Services module
        .gaming,
    ],
    cxxLanguageStandard: .cxx11
)

extension Product {
    static let basics = library(name: .basics, targets: [.Prefixed.basics])
    static let core = library(name: .core, targets: [.core, .Prefixed.core])
    static let login = library(name: .login, targets: [.login])
    static let share = library(name: .share, targets: [.share, .Prefixed.share])
    static let gaming = library(name: .gaming, targets: [.gaming, .Prefixed.gaming])
    static let aem = library(name: .aem, targets: [.aem, .Prefixed.aem])
}

extension Target {
    static let binarySource = BinarySource()

    static func binaryTarget(name: String, remoteChecksum: String) -> Target {
        switch binarySource {
        case .local:
            return .binaryTarget(
                name: name,
                path: localBinaryPath(for: name)
            )
        case .remote:
            return .binaryTarget(
                name: name,
                url: remoteBinaryURLString(for: name),
                checksum: remoteChecksum
            )
        }
    }

    static func localBinaryPath(for targetName: String) -> String {
        "build/XCFrameworks/Static/\(targetName).xcframework"
    }

    static func remoteBinaryURLString(for targetName: String) -> String {
        "https://github.com/facebook/facebook-ios-sdk/releases/download/v16.1.2/\(targetName)-Static_XCFramework.zip"
    }

    static let aem = target(name: .aem, dependencies: [.Prefixed.aem])

    static let core = target(
        name: .core,
        dependencies: [.aem, .Prefixed.basics, .Prefixed.core],
        linkerSettings: [
            .cPlusPlusLibrary,
            .zLibrary,
            .accelerateFramework,
        ]
    )

    static let login = target(name: .login, dependencies: [.core, .Prefixed.login])

    static let share = target(name: .share, dependencies: [.core, .Prefixed.share])

    static let gaming = target(name: .gaming, dependencies: [.Prefixed.gaming])

    enum Prefixed {
        static let basics = binaryTarget(
            name: .Prefixed.basics,
            remoteChecksum: "ba2fab4ac759fcb4aba48c25f1222c0839c697d7cb7de4a5e28befe26b0b3caa"
        )

        static let aem = binaryTarget(
            name: .Prefixed.aem,
            remoteChecksum: "8e9e1ef0aadbbd3e140822b0fc3313b011bb959c7cdad5f1c1745dd4e96d8c4c"
        )

        static let core = binaryTarget(
            name: .Prefixed.core,
            remoteChecksum: "6fecfd2342d00a8020bc371b00b25cdd020acebed5ab20ef796c81d52fbcad22"
        )

        static let login = binaryTarget(
            name: .Prefixed.login,
            remoteChecksum: "d98bd590683281d5c8c3ea900047c3586126465d34c3dc264b8d51c01019a328"
        )

        static let share = binaryTarget(
            name: .Prefixed.share,
            remoteChecksum: "42d7dce3155e3c9c9a7db29789e3606757ad7b29a4f7ad9da77afbbe9bc41bd3"
        )

        static let gamingServices = binaryTarget(
            name: .Prefixed.gaming,
            remoteChecksum: "91f7433eadf3257cf0ff2a80e3364d52dd5b542c72876f6cdc1e1f7a6aec556f"
        )
    }
}

extension Target.Dependency {
    static let aem = byName(name: .aem)
    static let core = byName(name: .core)

    enum Prefixed {
        static let aem = byName(name: .Prefixed.aem)
        static let basics = byName(name: .Prefixed.basics)
        static let core = byName(name: .Prefixed.core)
        static let login = byName(name: .Prefixed.login)
        static let share = byName(name: .Prefixed.share)
        static let gaming = byName(name: .Prefixed.gaming)
    }
}

extension LinkerSetting {
    static let cPlusPlusLibrary = linkedLibrary("c++")
    static let zLibrary = linkedLibrary("z")
    static let accelerateFramework = linkedFramework("Accelerate")
}

enum BinarySource {
    case local, remote

    init() {
        if getenv("USE_LOCAL_FB_BINARIES") != nil {
            self = .local
        } else {
            self = .remote
        }
    }
}

extension String {
    static let aem = "FacebookAEM"
    static let basics = "FacebookBasics"
    static let core = "FacebookCore"
    static let login = "FacebookLogin"
    static let share = "FacebookShare"
    static let gaming = "FacebookGamingServices"

    enum Prefixed {
        static let aem = "FBAEMKit"
        static let basics = "FBSDKCoreKit_Basics"
        static let core = "FBSDKCoreKit"
        static let login = "FBSDKLoginKit"
        static let share = "FBSDKShareKit"
        static let gaming = "FBSDKGamingServicesKit"
    }
}