/*
 *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#import "RTCMTLNSVideoView.h"

#import <Metal/Metal.h>
#import <MetalKit/MetalKit.h>

#import "base/RTCVideoFrame.h"

#import "RTCMTLI420Renderer.h"

@interface RTC_OBJC_TYPE (RTCMTLNSVideoView)
()<MTKViewDelegate> @property(nonatomic) id<RTCMTLRenderer> renderer;
@property(nonatomic, strong) MTKView *metalView;
@property(atomic, strong) RTC_OBJC_TYPE(RTCVideoFrame) * videoFrame;
@end

@implementation RTC_OBJC_TYPE (RTCMTLNSVideoView) {
  id<RTCMTLRenderer> _renderer;
}

@synthesize delegate = _delegate;
@synthesize renderer = _renderer;
@synthesize metalView = _metalView;
@synthesize videoFrame = _videoFrame;

- (instancetype)initWithFrame:(CGRect)frameRect {
  self = [super initWithFrame:frameRect];
  if (self) {
    [self configure];
  }
  return self;
}

- (instancetype)initWithCoder:(NSCoder *)aCoder {
  self = [super initWithCoder:aCoder];
  if (self) {
    [self configure];
  }
  return self;
}

#pragma mark - Private

+ (BOOL)isMetalAvailable {
  return [MTLCopyAllDevices() count] > 0;
}

- (void)configure {
  if ([[self class] isMetalAvailable]) {
    _metalView = [[MTKView alloc] initWithFrame:self.bounds];
    [self addSubview:_metalView];
    _metalView.layerContentsPlacement = NSViewLayerContentsPlacementScaleProportionallyToFit;
    _metalView.translatesAutoresizingMaskIntoConstraints = NO;
    _metalView.framebufferOnly = YES;
    _metalView.delegate = self;

    _renderer = [[RTCMTLI420Renderer alloc] init];
    if (![(RTCMTLI420Renderer *)_renderer addRenderingDestination:_metalView]) {
      _renderer = nil;
    };
  }
}

- (void)updateConstraints {
  NSDictionary *views = NSDictionaryOfVariableBindings(_metalView);

  NSArray *constraintsHorizontal =
      [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[_metalView]-0-|"
                                              options:0
                                              metrics:nil
                                                views:views];
  [self addConstraints:constraintsHorizontal];

  NSArray *constraintsVertical =
      [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[_metalView]-0-|"
                                              options:0
                                              metrics:nil
                                                views:views];
  [self addConstraints:constraintsVertical];
  [super updateConstraints];
}

#pragma mark - MTKViewDelegate methods
- (void)drawInMTKView:(nonnull MTKView *)view {
  if (self.videoFrame == nil) {
    return;
  }
  if (view == self.metalView) {
    [_renderer drawFrame:self.videoFrame];
  }
}

- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
}

#pragma mark - RTC_OBJC_TYPE(RTCVideoRenderer)

- (void)setSize:(CGSize)size {
  _metalView.drawableSize = size;
  dispatch_async(dispatch_get_main_queue(), ^{
    [self.delegate videoView:self didChangeVideoSize:size];
  });
  [_metalView draw];
}

- (void)renderFrame:(nullable RTC_OBJC_TYPE(RTCVideoFrame) *)frame {
  if (frame == nil) {
    return;
  }
  self.videoFrame = [frame newI420VideoFrame];
}

@end
