/*
 * Copyright 2018 Google
 *
 * 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 "FirebaseDynamicLinks/Sources/GINInvocation/GINInvocation.h"

#import "FirebaseDynamicLinks/Sources/GINInvocation/GINArgument.h"

@implementation GINInvocation

// A method that performs a selector on a target object, and return the result.
+ (id)objectByPerformingSelector:(SEL)selector
                        onTarget:(id)target
               numberOfArguments:(NSInteger)numberOfArguments, ... {
  if (![target respondsToSelector:selector]) {
#if DEBUG
    [NSException raise:@"InvalidSelectorException" format:@"Invalid selector send to target"];
#endif
    return nil;
  }

  NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
  NSInvocation *inv = [NSInvocation invocationWithMethodSignature:methodSignature];
  [inv setSelector:selector];
  [inv setTarget:target];

  int index = 2;
  va_list argumentList;

  va_start(argumentList, numberOfArguments);
  for (NSInteger i = 0; i < numberOfArguments; i++) {
    [GINArgument setNextArgumentInList:argumentList atIndex:index inInvocation:inv];
  }
  va_end(argumentList);

  [inv invoke];

  // This method only returns object.
  if ([methodSignature methodReturnLength]) {
    CFTypeRef result;
    [inv getReturnValue:&result];
    if (result) {
      CFRetain(result);
    }
    return (__bridge_transfer id)result;
  }
  return nil;
}

// A method that performs a selector on a target object, and return the result.
+ (double)doubleByPerformingSelector:(SEL)selector
                            onTarget:(id)target
                   numberOfArguments:(NSInteger)numberOfArguments, ... {
  if (![target respondsToSelector:selector]) {
#if DEBUG
    [NSException raise:@"InvalidSelectorException" format:@"Invalid selector send to target"];
#endif
    return 0;
  }

  NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
  NSInvocation *inv = [NSInvocation invocationWithMethodSignature:methodSignature];
  [inv setSelector:selector];
  [inv setTarget:target];

  int index = 2;
  va_list argumentList;

  va_start(argumentList, numberOfArguments);
  for (NSInteger i = 0; i < numberOfArguments; i++) {
    [GINArgument setNextArgumentInList:argumentList atIndex:index inInvocation:inv];
  }
  va_end(argumentList);

  [inv invoke];

  // This method only returns double.
  if ([methodSignature methodReturnLength]) {
    double doubleValue;
    [inv getReturnValue:&doubleValue];
    return doubleValue;
  }
  return 0;
}

@end
