 import Foundation
 import StoreKit
 
-public typealias RestoreTransactionsCompletionBlock = NSError? -> Void
-public typealias LoadProductsCompletionBlock = (Array<SKProduct>?, NSError?) -> Void
-public typealias PurchaseProductCompletionBlock = (NSError?) -> Void
+public typealias RestoreTransactionsCompletionBlock = (Error?) -> Void
+public typealias LoadProductsCompletionBlock = (Array<SKProduct>?, Error?) -> Void
+public typealias PurchaseProductCompletionBlock = (Error?) -> Void
 public typealias LoadProductsRequestInfo = (request: SKProductsRequest, completion: LoadProductsCompletionBlock)
 public typealias PurchaseProductRequestInfo = (productId: String, completion: PurchaseProductCompletionBlock)
 
 public class IAPManager: NSObject {
-    public static let sharedManager = IAPManager()
+    public static let shared = IAPManager()
     
     override init() {
         super.init()
         
         restorePurchasedItems()
         
-        SKPaymentQueue.defaultQueue().addTransactionObserver(self)
-        
-        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("savePurchasedItems"), name: UIApplicationDidEnterBackgroundNotification, object: nil)
+        SKPaymentQueue.default().add(self)
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(IAPManager.savePurchasedItems), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
     }
     
     public func canMakePayments() -> Bool {

        }
    }
    
    private var availableProducts = Dictionary<String, SKProduct>()
    private var purchasedProductIds = Array<String>()
    private var restoreTransactionsCompletionBlock: RestoreTransactionsCompletionBlock?
    private var loadProductsRequests = Array<LoadProductsRequestInfo>()
    private var purchaseProductRequests = Array<PurchaseProductRequestInfo>()
    
}

extension IAPManager: SKProductsRequestDelegate {
    public func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
        for product in response.products {
            availableProducts[product.productIdentifier] = product
        }
        
        callLoadProductsCompletionForRequest(request, responseProducts: response.products, error: nil)
    }
    
    public func request(request: SKRequest, didFailWithError error: NSError) {
        if let productRequest = request as? SKProductsRequest {
            callLoadProductsCompletionForRequest(productRequest, responseProducts: nil, error: error)
        }
    }
}

extension IAPManager: SKPaymentTransactionObserver {
    
    public func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            let productId = transaction.payment.productIdentifier
            switch transaction.transactionState {
            case .Restored: fallthrough
            case .Purchased:
                purchasedProductIds.append(productId)
                savePurchasedItems()
                
                callPurchaseProductCompletionForProduct(productId, error: nil)
                queue.finishTransaction(transaction)
            case .Failed:
                callPurchaseProductCompletionForProduct(productId, error: transaction.error)
                queue.finishTransaction(transaction)
            case .Purchasing:
                print("Purchasing \(productId)...")
            default: break
            }
        }
    }
    
    public func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
        if let completion = restoreTransactionsCompletionBlock {
            completion(nil)
        }
    }
    
    public func paymentQueue(queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: NSError) {
        if let completion = restoreTransactionsCompletionBlock {
            completion(error)
        }

        return purchasedProductIds.contains(productId)
    }
    
    public func restoreCompletedTransactions(completion: @escaping RestoreTransactionsCompletionBlock) {
        restoreTransactionsCompletionBlock = completion
        SKPaymentQueue.default().restoreCompletedTransactions()
    }
    
    public func loadProducts(productIds: Array<String>, completion:@escaping LoadProductsCompletionBlock) {
        var loadedProducts = Array<SKProduct>()
        var remainingIds = Array<String>()
        
