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

#import <StoreKit/StoreKit.h>

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

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

void onEventInMainThread(uv_async_t* handle);

@interface VanaPaymentHandler : NSObject <SKPaymentTransactionObserver, SKRequestDelegate> {
  @private
    Nan::Callback* callback_;
    NSString* errorMessage;
    BOOL isTranscationObserver;
    SKReceiptRefreshRequest * refreshRequest;
    uv_async_t async; // keep this instance around for as long as we might need to do the periodic callback
}

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

@implementation VanaPaymentHandler

- (void)paymentQueue:(SKPaymentQueue *)queue
 updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {}
- (void)paymentQueue:(SKPaymentQueue *)queue
 removedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {}

- (void)paymentQueue:(SKPaymentQueue *)queue
restoreCompletedTransactionsFailedWithError:(NSError *)error {
  errorMessage = [error localizedDescription];
  uv_async_send(&async);
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
  errorMessage = NULL;
  uv_async_send(&async);
}

- (void)paymentQueue:(SKPaymentQueue *)queue
    updatedDownloads:(NSArray<SKDownload *> *)downloads { }

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

 - (BOOL)paymentQueue:(SKPaymentQueue *)queue
shouldAddStorePayment:(SKPayment *)payment
          forProduct:(SKProduct *)product { return YES; }

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

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

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

  return self;
}

- (void) restoreCompletedTransactions {
  isTranscationObserver = YES;
  SKPaymentQueue * q = [SKPaymentQueue defaultQueue];
  [q addTransactionObserver: self];
  [q restoreCompletedTransactions];
}

- (void) refreshReceipt {
  isTranscationObserver = NO;
  refreshRequest = [[SKReceiptRefreshRequest alloc] init];
  refreshRequest.delegate = self;
  [refreshRequest start];
}

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

  if (errorMessage == NULL) {
    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;

  if (isTranscationObserver) {
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
  }

  refreshRequest = NULL;

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

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