/**
 * Copyright (c) 2015-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 <AppKit/AppKit.h>

#import "RCTViewManager.h"

#import "RCTBridge.h"
#import "RCTBorderStyle.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTShadowView.h"
#import "RCTUIManager.h"
#import "RCTUtils.h"
#import "RCTView.h"
#import "NSView+React.h"

//@implementation RCTConvert(UIAccessibilityTraits)
//
//RCT_MULTI_ENUM_CONVERTER(UIAccessibilityTraits, (@{
//  @"none": @(UIAccessibilityTraitNone),
//  @"button": @(UIAccessibilityTraitButton),
//  @"link": @(UIAccessibilityTraitLink),
//  @"header": @(UIAccessibilityTraitHeader),
//  @"search": @(UIAccessibilityTraitSearchField),
//  @"image": @(UIAccessibilityTraitImage),
//  @"selected": @(UIAccessibilityTraitSelected),
//  @"plays": @(UIAccessibilityTraitPlaysSound),
//  @"key": @(UIAccessibilityTraitKeyboardKey),
//  @"text": @(UIAccessibilityTraitStaticText),
//  @"summary": @(UIAccessibilityTraitSummaryElement),
//  @"disabled": @(UIAccessibilityTraitNotEnabled),
//  @"frequentUpdates": @(UIAccessibilityTraitUpdatesFrequently),
//  @"startsMedia": @(UIAccessibilityTraitStartsMediaSession),
//  @"adjustable": @(UIAccessibilityTraitAdjustable),
//  @"allowsDirectInteraction": @(UIAccessibilityTraitAllowsDirectInteraction),
//  @"pageTurn": @(UIAccessibilityTraitCausesPageTurn),
//}), UIAccessibilityTraitNone, unsignedLongLongValue)
//
//@end

@implementation RCTViewManager

@synthesize bridge = _bridge;

RCT_EXPORT_MODULE()

- (dispatch_queue_t)methodQueue
{
  RCTAssert(_bridge, @"Bridge not set");
  RCTAssert(_bridge.uiManager || !_bridge.valid, @"UIManager not initialized");
  RCTAssert(_bridge.uiManager.methodQueue || !_bridge.valid, @"UIManager.methodQueue not initialized");
  return _bridge.uiManager.methodQueue;
}

- (NSView *)view
{
  return [RCTView new];
}

- (RCTShadowView *)shadowView
{
  return [RCTShadowView new];
}

- (NSArray<NSString *> *)customBubblingEventTypes
{
  return @[

    // Generic events
    @"press",
    @"change",
    @"focus",
    @"blur",
    @"submitEditing",
    @"endEditing",
    @"keyPress",

    // Touch events
    @"touchStart",
    @"touchMove",
    @"touchCancel",
    @"touchEnd",

    // Mouse events
    @"mouseEnter",
    @"mouseLeave",
  ];
}

- (NSArray<NSString *> *)customDirectEventTypes
{
  return @[];
}

- (NSDictionary<NSString *, id> *)constantsToExport
{
  return @{@"forceTouchAvailable": @(RCTForceTouchAvailable())};
}

- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(__unused RCTShadowView *)shadowView
{
  return nil;
}

- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictionary<NSNumber *, RCTShadowView *> *)shadowViewRegistry
{
  return nil;
}

- (void)checkLayerExists:(NSView *)view
{
  if (!view.layer) {
    CALayer *viewLayer = [CALayer layer];
    viewLayer.delegate = view;
    [view setLayer:viewLayer];
    [view setWantsLayer:YES];
  }
}

#pragma mark - View properties

RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel, NSString)
RCT_EXPORT_VIEW_PROPERTY(accessibilityTraits, UIAccessibilityTraits)
RCT_EXPORT_VIEW_PROPERTY(backgroundColor, NSColor)
RCT_REMAP_VIEW_PROPERTY(accessible, accessibilityElement, BOOL)
RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier, NSString)
RCT_REMAP_VIEW_PROPERTY(testRole, accessibilityRole, NSString)
RCT_REMAP_VIEW_PROPERTY(backfaceVisibility, layer.doubleSided, css_backface_visibility_t)
RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor);
RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset, CGSize);
RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity, float)
RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius, CGFloat)
RCT_REMAP_VIEW_PROPERTY(overflow, clipsToBounds, css_clip_t)
RCT_REMAP_VIEW_PROPERTY(toolTip, toolTip, NSString)
RCT_CUSTOM_VIEW_PROPERTY(shouldRasterizeIOS, BOOL, RCTView)
{
  view.layer.shouldRasterize = json ? [RCTConvert BOOL:json] : defaultView.layer.shouldRasterize;
  view.layer.rasterizationScale = view.layer.shouldRasterize ? [NSScreen mainScreen].backingScaleFactor : defaultView.layer.rasterizationScale;
}
RCT_CUSTOM_VIEW_PROPERTY(draggedTypes, NSArray*<NSString *>, RCTView)
{
  if (json) {
    NSArray *types = [RCTConvert NSArray:json];
    [view registerForDraggedTypes:types];
  } else {
    [view registerForDraggedTypes:defaultView.registeredDraggedTypes];
  }
}
RCT_CUSTOM_VIEW_PROPERTY(opacity, float, RCTView)
{
  if (json) {
    [self checkLayerExists:view];
    [view.layer setOpacity:[RCTConvert float:json]];
  } else {
    [view.layer setOpacity:defaultView.layer.opacity];
  }
}
RCT_CUSTOM_VIEW_PROPERTY(transformMatrix, CATransform3D, RCTView)
{
  CATransform3D transform = json ? [RCTConvert CATransform3D:json] : defaultView.layer.transform;
  if ([view respondsToSelector:@selector(shouldBeTransformed)] && !view.superview) {
    view.shouldBeTransformed = YES;
    view.transform = transform;
  } else {
    view.layer.transform = transform;
  }

  // TODO: Improve this by enabling edge antialiasing only for transforms with rotation or skewing
  view.layer.edgeAntialiasingMask = !CATransform3DIsIdentity(transform);
}

RCT_CUSTOM_VIEW_PROPERTY(removeClippedSubviews, BOOL, RCTView)
{
  if ([view respondsToSelector:@selector(setRemoveClippedSubviews:)]) {
    view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews;
  }
}
RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView) {
  if ([view respondsToSelector:@selector(setBorderRadius:)]) {
    [self checkLayerExists:view];
    view.borderRadius = json ? [RCTConvert CGFloat:json] : defaultView.borderRadius;
  } else {
    view.layer.cornerRadius = json ? [RCTConvert CGFloat:json] : defaultView.layer.cornerRadius;
  }
}
RCT_CUSTOM_VIEW_PROPERTY(borderColor, CGColor, RCTView)
{
  if ([view respondsToSelector:@selector(setBorderColor:)]) {
    [self checkLayerExists:view];
    view.borderColor = json ? [RCTConvert CGColor:json] : defaultView.borderColor;
  } else {
    view.layer.borderColor = json ? [RCTConvert CGColor:json] : defaultView.layer.borderColor;
  }
}
RCT_CUSTOM_VIEW_PROPERTY(borderWidth, CGFloat, RCTView)
{
  if ([view respondsToSelector:@selector(setBorderWidth:)]) {
    [self checkLayerExists:view];
    view.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.borderWidth;
  } else {
    view.layer.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.layer.borderWidth;
  }
}
RCT_CUSTOM_VIEW_PROPERTY(borderStyle, RCTBorderStyle, RCTView)
{
  if ([view respondsToSelector:@selector(setBorderStyle:)]) {
    view.borderStyle = json ? [RCTConvert RCTBorderStyle:json] : defaultView.borderStyle;
  }
}
RCT_EXPORT_VIEW_PROPERTY(onAccessibilityTap, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onMagicTap, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onDragEnter, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onDragLeave, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onDrop, RCTDirectEventBlock)

#define RCT_VIEW_BORDER_PROPERTY(SIDE)                                  \
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Width, CGFloat, RCTView)         \
{                                                                       \
  if ([view respondsToSelector:@selector(setBorder##SIDE##Width:)]) {   \
    [self checkLayerExists:view];                                       \
    view.border##SIDE##Width = json ? [RCTConvert CGFloat:json] : defaultView.border##SIDE##Width; \
  }                                                                     \
}                                                                       \
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Color, NSColor, RCTView)         \
{                                                                       \
  if ([view respondsToSelector:@selector(setBorder##SIDE##Color:)]) {   \
    [self checkLayerExists:view];                                       \
    view.border##SIDE##Color = json ? [RCTConvert CGColor:json] : defaultView.border##SIDE##Color; \
  }                                                                     \
}

RCT_VIEW_BORDER_PROPERTY(Top)
RCT_VIEW_BORDER_PROPERTY(Right)
RCT_VIEW_BORDER_PROPERTY(Bottom)
RCT_VIEW_BORDER_PROPERTY(Left)

#define RCT_VIEW_BORDER_RADIUS_PROPERTY(SIDE)                           \
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Radius, CGFloat, RCTView)        \
{                                                                       \
  if ([view respondsToSelector:@selector(setBorder##SIDE##Radius:)]) {  \
    view.border##SIDE##Radius = json ? [RCTConvert CGFloat:json] : defaultView.border##SIDE##Radius; \
  }                                                                     \
}                                                                       \

RCT_VIEW_BORDER_RADIUS_PROPERTY(TopLeft)
RCT_VIEW_BORDER_RADIUS_PROPERTY(TopRight)
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomLeft)
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomRight)

#pragma mark - ShadowView properties

RCT_EXPORT_SHADOW_PROPERTY(backgroundColor, NSColor)

RCT_EXPORT_SHADOW_PROPERTY(top, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(right, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(bottom, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(left, CGFloat);

RCT_EXPORT_SHADOW_PROPERTY(width, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(height, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(minWidth, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(minHeight, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(maxWidth, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(maxHeight, CGFloat)

RCT_EXPORT_SHADOW_PROPERTY(borderTopWidth, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(borderRightWidth, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(borderBottomWidth, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(borderLeftWidth, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(borderWidth, CGFloat)

RCT_EXPORT_SHADOW_PROPERTY(marginTop, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(marginRight, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(marginBottom, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(marginLeft, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(marginVertical, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(marginHorizontal, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(margin, CGFloat)

RCT_EXPORT_SHADOW_PROPERTY(paddingTop, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(paddingRight, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(paddingBottom, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(paddingLeft, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(paddingVertical, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(paddingHorizontal, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(padding, CGFloat)

RCT_EXPORT_SHADOW_PROPERTY(flex, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(flexDirection, css_flex_direction_t)
RCT_EXPORT_SHADOW_PROPERTY(flexWrap, css_wrap_type_t)
RCT_EXPORT_SHADOW_PROPERTY(justifyContent, css_justify_t)
RCT_EXPORT_SHADOW_PROPERTY(alignItems, css_align_t)
RCT_EXPORT_SHADOW_PROPERTY(alignSelf, css_align_t)
RCT_EXPORT_SHADOW_PROPERTY(position, css_position_type_t)

RCT_EXPORT_SHADOW_PROPERTY(onLayout, RCTDirectEventBlock)

@end
