/*
 *  Copyright (c) 2012 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.
 */

#define DEFAULT_CAPTURE_DEVICE_INDEX    1
#define DEFAULT_FRAME_RATE              30
#define DEFAULT_FRAME_WIDTH             352
#define DEFAULT_FRAME_HEIGHT            288
#define ROTATE_CAPTURED_FRAME           1
#define LOW_QUALITY                     1

#import "webrtc/modules/video_capture/mac/qtkit/video_capture_qtkit_objc.h"

#include "webrtc/system_wrappers/include/trace.h"

using namespace webrtc;
using namespace videocapturemodule;

@implementation VideoCaptureMacQTKitObjC

-(id)init {
  self = [super init];
  if (self) {
    [self initializeVariables];
  }
  return self;
}

- (void)dealloc {
  if (_captureSession)
    [_captureSession stopRunning];

  if (_captureVideoDeviceInput) {
    if ([[_captureVideoDeviceInput device] isOpen])
      [[_captureVideoDeviceInput device] close];

    [_captureVideoDeviceInput release];
  }

  [_captureDecompressedVideoOutput release];
  [_captureSession release];
  [_captureDevices release];
  [_lock release];

  [super dealloc];
}

#pragma mark Public methods

- (void)registerOwner:(VideoCaptureMacQTKit*)owner {
  [_lock lock];
  _owner = owner;
  [_lock unlock];
}

- (BOOL)setCaptureDeviceById:(char*)uniqueId {
  if (uniqueId == nil || !strcmp("", uniqueId)) {
    WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0,
                 "Incorrect capture id argument");
    return NO;
  }

  if (!strcmp(uniqueId, _captureDeviceNameUniqueID))
    return YES;

  QTCaptureDevice* captureDevice;
  for(int index = 0; index < _captureDeviceCount; index++) {
    captureDevice = (QTCaptureDevice*)[_captureDevices objectAtIndex:index];
    char captureDeviceId[1024] = "";
    [[captureDevice uniqueID] getCString:captureDeviceId
                               maxLength:1024
                                encoding:NSUTF8StringEncoding];
    if (strcmp(uniqueId, captureDeviceId) == 0) {
      WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0,
                   "%s:%d Found capture device id %s as index %d",
                   __FUNCTION__, __LINE__, captureDeviceId, index);
      [[captureDevice localizedDisplayName] getCString:_captureDeviceNameUTF8
                                             maxLength:1024
                                              encoding:NSUTF8StringEncoding];
      [[captureDevice uniqueID] getCString:_captureDeviceNameUniqueID
                                 maxLength:1024
                                  encoding:NSUTF8StringEncoding];
      break;
    }
    captureDevice = nil;
  }

  if (!captureDevice)
    return NO;

  NSError* error;
  if (![captureDevice open:&error]) {
    WEBRTC_TRACE(kTraceError, kTraceVideoCapture, 0,
                 "Failed to open capture device: %s", _captureDeviceNameUTF8);
    return NO;
  }

  if (_captureVideoDeviceInput) {
    [_captureVideoDeviceInput release];
  }
  _captureVideoDeviceInput =
      [[QTCaptureDeviceInput alloc] initWithDevice:captureDevice];

  if (![_captureSession addInput:_captureVideoDeviceInput error:&error]) {
    WEBRTC_TRACE(kTraceError, kTraceVideoCapture, 0,
                 "Failed to add input from %s to the capture session",
                 _captureDeviceNameUTF8);
    return NO;
  }

  WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0,
               "%s:%d successfully added capture device: %s", __FUNCTION__,
               __LINE__, _captureDeviceNameUTF8);
  return YES;
}

- (void)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate {
  _frameWidth = width;
  _frameHeight = height;
  _frameRate = frameRate;

  NSDictionary* captureDictionary =
      [NSDictionary dictionaryWithObjectsAndKeys:
          [NSNumber numberWithDouble:_frameWidth],
          (id)kCVPixelBufferWidthKey,
          [NSNumber numberWithDouble:_frameHeight],
          (id)kCVPixelBufferHeightKey,
          [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32ARGB],
          (id)kCVPixelBufferPixelFormatTypeKey,
          nil];
  [_captureDecompressedVideoOutput
      performSelectorOnMainThread:@selector(setPixelBufferAttributes:)
                       withObject:captureDictionary
                    waitUntilDone:YES];
}

- (void)startCapture {
  if (_capturing)
    return;

  [_captureSession startRunning];
  _capturing = YES;
}

- (void)stopCapture {
  if (!_capturing)
    return;

  [_captureSession stopRunning];
  _capturing = NO;
}

#pragma mark Private methods

- (BOOL)initializeVariables {
  if (NSClassFromString(@"QTCaptureSession") == nil)
    return NO;

  memset(_captureDeviceNameUTF8, 0, 1024);
  _framesDelivered = 0;
  _framesRendered = 0;
  _captureDeviceCount = 0;
  _capturing = NO;
  _captureInitialized = NO;
  _frameRate = DEFAULT_FRAME_RATE;
  _frameWidth = DEFAULT_FRAME_WIDTH;
  _frameHeight = DEFAULT_FRAME_HEIGHT;
  _lock = [[NSLock alloc] init];
  _captureSession = [[QTCaptureSession alloc] init];
  _captureDecompressedVideoOutput =
      [[QTCaptureDecompressedVideoOutput alloc] init];
  [_captureDecompressedVideoOutput setDelegate:self];

  [self getCaptureDevices];
  if (![self initializeVideoCapture])
    return NO;

  return NO;
}

- (void)getCaptureDevices {
  if (_captureDevices)
    [_captureDevices release];

  _captureDevices = [[NSArray alloc] initWithArray:
      [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]];

  _captureDeviceCount = _captureDevices.count;
}

- (BOOL)initializeVideoCapture{
  NSDictionary *captureDictionary =
      [NSDictionary dictionaryWithObjectsAndKeys:
          [NSNumber numberWithDouble:_frameWidth],
          (id)kCVPixelBufferWidthKey,
          [NSNumber numberWithDouble:_frameHeight],
          (id)kCVPixelBufferHeightKey,
          [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32ARGB],
          (id)kCVPixelBufferPixelFormatTypeKey,
          nil];

  [_captureDecompressedVideoOutput setPixelBufferAttributes:captureDictionary];
  [_captureDecompressedVideoOutput setAutomaticallyDropsLateVideoFrames:YES];
  [_captureDecompressedVideoOutput
      setMinimumVideoFrameInterval:(NSTimeInterval)1/(float)_frameRate];

  NSError *error;
  if (![_captureSession addOutput:_captureDecompressedVideoOutput error:&error])
    return NO;

  return YES;
}

- (void)captureOutput:(QTCaptureOutput *)captureOutput
    didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer
                       fromConnection:(QTCaptureConnection *)connection {
  // TODO(mflodman) Experiment more when this happens.
}

- (void)captureOutput:(QTCaptureOutput *)captureOutput
  didOutputVideoFrame:(CVImageBufferRef)videoFrame
     withSampleBuffer:(QTSampleBuffer *)sampleBuffer
       fromConnection:(QTCaptureConnection *)connection {

  [_lock lock];
  if (!_owner) {
    [_lock unlock];
    return;
  }

  const int kFlags = 0;
  if (CVPixelBufferLockBaseAddress(videoFrame, kFlags) == kCVReturnSuccess) {
    void *baseAddress = CVPixelBufferGetBaseAddress(videoFrame);
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(videoFrame);
    size_t frameHeight = CVPixelBufferGetHeight(videoFrame);
    size_t frameSize = bytesPerRow * frameHeight;

    VideoCaptureCapability tempCaptureCapability;
    tempCaptureCapability.width = _frameWidth;
    tempCaptureCapability.height = _frameHeight;
    tempCaptureCapability.maxFPS = _frameRate;
    // TODO(wu) : Update actual type and not hard-coded value.
    tempCaptureCapability.rawType = kVideoBGRA;

    _owner->IncomingFrame((unsigned char*)baseAddress, frameSize,
                          tempCaptureCapability, 0);
    CVPixelBufferUnlockBaseAddress(videoFrame, kFlags);
  }
  [_lock unlock];
  _framesDelivered++;
  _framesRendered++;
}

@end
