/*
 *  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 "CKComponentScopeHandle.h"
#include <atomic>

#include <mutex>

#import <ComponentKit/CKInternalHelpers.h>
#import <ComponentKit/CKTreeNode.h>
#import <ComponentKit/CKMutex.h>

#import "CKComponentScopeRoot.h"
#import "CKComponentSubclass.h"
#import "CKComponentInternal.h"
#import "CKComponentProtocol.h"
#import "CKComponentControllerProtocol.h"
#import "CKThreadLocalComponentScope.h"
#import "CKRenderComponentProtocol.h"

@interface CKScopedResponder ()
- (void)addHandleToChain:(CKComponentScopeHandle *)component;
@end

@implementation CKComponentScopeHandle
{
  id<CKComponentStateListener> __weak _listener;
  id<CKComponentControllerProtocol> _controller;
  CKComponentScopeRootIdentifier _rootIdentifier;
  BOOL _acquired;
  BOOL _resolved;
  CKScopedResponder *_scopedResponder;
}

- (instancetype)initWithListener:(id<CKComponentStateListener>)listener
                  rootIdentifier:(CKComponentScopeRootIdentifier)rootIdentifier
               componentTypeName:(const char *)componentTypeName
                    initialState:(id)initialState
{
  static std::atomic_int32_t nextGlobalIdentifier;
  return [self initWithListener:listener
               globalIdentifier:++nextGlobalIdentifier
                 rootIdentifier:rootIdentifier
              componentTypeName:componentTypeName
                          state:initialState
                     controller:nil  // Controllers are built on resolution of the handle.
                scopedResponder:nil];  // Scoped responders are created lazily. Once they exist, we use that reference for future handles.
}

- (instancetype)initWithListener:(id<CKComponentStateListener>)listener
                globalIdentifier:(CKComponentScopeHandleIdentifier)globalIdentifier
                  rootIdentifier:(CKComponentScopeRootIdentifier)rootIdentifier
               componentTypeName:(const char *)componentTypeName
                           state:(id)state
                      controller:(id<CKComponentControllerProtocol>)controller
                 scopedResponder:(CKScopedResponder *)scopedResponder
{
  if (self = [super init]) {
    _listener = listener;
    _globalIdentifier = globalIdentifier;
    _rootIdentifier = rootIdentifier;
    _componentTypeName = componentTypeName;
    _state = state;
    _controller = controller;

    _scopedResponder = scopedResponder;
    [scopedResponder addHandleToChain:self];
  }
  return self;
}

- (instancetype)newStatelessHandle
{
  const auto handle = [[CKComponentScopeHandle alloc] initWithListener:_listener
                                                      globalIdentifier:_globalIdentifier
                                                        rootIdentifier:_rootIdentifier
                                                     componentTypeName:_componentTypeName
                                                                 state:nil
                                                            controller:nil
                                                       scopedResponder:nil];
  // This handle isn't meant to be resolved, marking as `YES`.
  handle->_resolved = YES;
  return handle;
}

- (instancetype)newHandleWithStateUpdates:(const CKComponentStateUpdateMap &)stateUpdates
{
  id updatedState = _state;
  const auto pendingUpdatesIt = stateUpdates.find(self);
  if (pendingUpdatesIt != stateUpdates.end()) {
    for (auto pendingUpdate: pendingUpdatesIt->second) {
      if (pendingUpdate != nil) {
        updatedState = pendingUpdate(updatedState);
      }
    }
  }

  return [[CKComponentScopeHandle alloc] initWithListener:_listener
                                         globalIdentifier:_globalIdentifier
                                           rootIdentifier:_rootIdentifier
                                        componentTypeName:_componentTypeName
                                                    state:updatedState
                                               controller:_controller
                                          scopedResponder:_scopedResponder];
}

- (id<CKComponentControllerProtocol>)controller
{
  RCAssert(_resolved, @"Requesting controller from scope handle before resolution. The controller will be nil.");
  return _controller;
}

- (void)dealloc
{
  RCAssert(_resolved, @"Must be resolved before deallocation.");
}

#pragma mark - State

- (void)updateState:(id (^)(id))updateBlock
           metadata:(const CKStateUpdateMetadata &)metadata
               mode:(CKUpdateMode)mode
{
  RCAssertNotNil(updateBlock, @"The update block cannot be nil");
  if (![NSThread isMainThread] && [(id<CKComponentStateListener>)[_listener class] requiresMainThreadAffinedStateUpdates]) {
    // Passing a const& into a block is scary, make a local copy to be safe.
    const auto metadataCopy = metadata;
    dispatch_async(dispatch_get_main_queue(), ^{
      [self updateState:updateBlock metadata:metadataCopy mode:mode];
    });
    return;
  }
  [_listener componentScopeHandle:self
                   rootIdentifier:_rootIdentifier
            didReceiveStateUpdate:updateBlock
                         metadata:metadata
                             mode:mode];
}

- (void)replaceState:(id)state
{
  RCAssertFalse(_resolved);
  _state = state;
}

#pragma mark - Component Scope Handle Acquisition

- (BOOL)acquireFromComponent:(id<CKComponentProtocol>)component
{
  if (!_acquired && component.typeName == _componentTypeName) {
    _acquired = YES;
    _acquiredComponent = component;
    return YES;
  } else {
    return NO;
  }
}

- (void)relinquishComponent
{
  _acquiredComponent = nil;
}

- (void)forceAcquireFromComponent:(id<CKComponentProtocol>)component
{
  RCAssert(component.typeName == _componentTypeName, @"%s has to be a member of %s class", component.typeName, _componentTypeName);
  RCAssert(!_acquired, @"scope handle cannot be acquired twice");
  _acquired = YES;
  _acquiredComponent = component;
}

- (void)setTreeNode:(CKTreeNode *)treeNode
{
  RCAssertWithCategory(_treeNodeIdentifier == 0,
                       NSStringFromClass([_acquiredComponent class]),
                       @"_treeNodeIdentifier cannot be set twice");
  _treeNodeIdentifier = treeNode.nodeIdentifier;
  _treeNode = treeNode;
}

- (void)resolveAndRegisterInScopeRoot:(CKComponentScopeRoot *)scopeRoot
{
  [self resolveInScopeRoot:scopeRoot];
  [self registerInScopeRoot:scopeRoot];
}

- (void)resolveInScopeRoot:(CKComponentScopeRoot *)scopeRoot
{
  RCAssertFalse(_resolved);

  // Strong ref: _acquiredComponent may be nil when rendering-to-nil as the
  // handle won't be acquired.
  const auto acquiredComponent = _acquiredComponent;
  if (acquiredComponent != nil && _controller == nil) {
    // Build the controller on the first non nil component.
    _controller = [acquiredComponent buildController];
  }

  _resolved = YES;
}

- (void)registerInScopeRoot:(CKComponentScopeRoot *)scopeRoot
{
  // Register after scope handle resolution so the controller can be accessed
  // in the predicates.
  [scopeRoot registerComponent:_acquiredComponent];
  [scopeRoot registerComponentController:_controller];
}

- (CKScopedResponder *)scopedResponder
{
  if (!_scopedResponder) {
    _scopedResponder = [CKScopedResponder new];
    [_scopedResponder addHandleToChain:self];
  }

  return _scopedResponder;
}

@end

@implementation CKScopedResponder
{
  std::vector<__weak CKComponentScopeHandle *> _handles;
  std::mutex _mutex;
}

- (void)addHandleToChain:(CKComponentScopeHandle *)handle
{
  if (!handle) {
    return;
  }

  std::lock_guard<std::mutex> l(_mutex);
  _handles.push_back(handle);
}

- (CKScopedResponderKey)keyForHandle:(CKComponentScopeHandle *)handle
{
  static const CKScopedResponderKey notFoundKey = INT_MAX;

  if (handle == nil) {
    return notFoundKey;
  }

  std::lock_guard<std::mutex> l(_mutex);
  auto result = CK::find(_handles, handle);

  if (result == _handles.end()) {
    RCFailAssert(@"This scope handle is not associated with this Responder.");
    return notFoundKey;
  }

  // Returning the index of an element in a vector: https://stackoverflow.com/a/15099743
  return (int)std::distance(_handles.begin(), result);
}

- (id)responderForKey:(CKScopedResponderKey)key
{
  std::lock_guard<std::mutex> l(_mutex);

  const size_t numberOfHandles = _handles.size();
  if (key < 0 || key >= numberOfHandles) {
    RCFailAssert(@"Invalid key \"%d\" for responder with %zu handles", key, numberOfHandles);
    return nil;
  }

  for (int i = key; i < numberOfHandles; i++) {
      const auto handle = _handles[i];
      const id<CKComponentProtocol> responder = handle.acquiredComponent;
      if (responder != nil) {
        return responder;
      }
  }

  return nil;
}

@end
