
/*******************************************************
 * Copyright (C) 2018 XMind Ltd. - All Rights Reserved
 *******************************************************/

#import <StoreKit/StoreKit.h>

#import <nan.h>
#import <string>
#import <vector>
#import <uv.h>

static NSMutableSet* _purchaseCache = NULL;
static NSMutableSet* getPurchaseCache() {
  if (_purchaseCache == NULL) {
    _purchaseCache = [[NSMutableSet alloc] init];
  }
  return _purchaseCache;
}

void onPurchaseInMainThread(uv_async_t* handle);

@interface VanaInAppPurchase : NSObject <SKProductsRequestDelegate> {
  @private
    Nan::Callback* callback_;
    NSInteger quantity_;
    uv_async_t async; // keep this instance around for as long as we might need to do the periodic callback
    NSString* errorMessage;
    bool isValidProduct;
}

- (id)initWithCallback:(Nan::Callback*) callback quantity: (NSInteger) quantity;
@end

@implementation VanaInAppPurchase

- (id)initWithCallback: (Nan::Callback*)callback quantity: (NSInteger)quantity {
  if ((self = [super init])) {
    callback_ = callback;
    quantity_ = quantity;

    // Add the product into cache, otherwise, it will be ARC-ed.
    NSMutableSet* cache = getPurchaseCache();
    @synchronized(cache) {
      [cache addObject:self];
    }

    uv_async_init(uv_default_loop(), &async, onPurchaseInMainThread);
    async.data = (__bridge void*) self;
  }

  return self;
}

- (void) onPurchase {
  Nan::HandleScope scope;

  if (isValidProduct) {
    v8::Local<v8::Value> argv[] = { Nan::New(true) };
    if (callback_ != NULL) { callback_->Call(1, argv); }
  } else {
    v8::Local<v8::Value> argv[] = { Nan::New(false), Nan::New([errorMessage UTF8String]).ToLocalChecked() };
    if (callback_ != NULL) { callback_->Call(2, argv); }
  }

  uv_close((uv_handle_t*) &async, NULL);

  if (callback_ != NULL) {
    delete callback_;
    callback_ = NULL;
  }
  async.data = NULL;

  NSMutableSet* cache = getPurchaseCache();
  @synchronized(cache) {
    [cache removeObject:self];
  }
}

- (void)purchaseProduct:(NSString *)productID {
  isValidProduct = false;

  SKProductsRequest* productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:
    [NSSet setWithObject:productID]
  ];

  productsRequest.delegate = self;
  [productsRequest start];
}

- (void)productsRequest:(SKProductsRequest*)request
     didReceiveResponse:(SKProductsResponse*)response {
  // Get the products.
  SKProduct* product = [response.products count] == 1 ? [response.products firstObject] : nil;

  if (product == nil) {
    errorMessage = @"InvalidProduct";
  } else {
    SKMutablePayment* payment = [SKMutablePayment paymentWithProduct:product];
    payment.quantity = quantity_;
    [[SKPaymentQueue defaultQueue] addPayment:payment];

    // Notify that the payment has been added to the queue with success.
    isValidProduct = true;
  }

  uv_async_send(&async);
}

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
  errorMessage = [error localizedDescription];
  uv_async_send(&async);
}
@end

void onPurchaseInMainThread(uv_async_t* handle) {
  [(__bridge id) handle->data onPurchase];
}
