     super.init()
   }
 
+
   override func run() {
-
     safeDataSource.dequeuPendingChangesAndOnComplete { changes in
+
       dispatch_async(dispatch_get_main_queue()) {
+        // TODO: Can make this logic more efficient: Have to process deletions first because batch 
+        //   updates will also apply deletions first so, if we haven't added the item we want to delete yet,
+        //   the batch insertion will fail
+
         // Got changes, now head on over to collection view
+        // We first apply all additions and then on complete we apply deletions and on complete of deletions we mark operation complete
+
+
+        // TODO: Do all this math in a concurrent queue and only get on main queue once set of changes are calculated
+
+        // Range of existing items in data source array before any changes applied in this op
+
+
+        let sectionStartingRanges = self.safeDataSource.dataSourceItems.enumerate().map { index, items in
+          return 0..<self.safeDataSource.dataSourceItems[0].count
+        }
+
+        var sectionAdditions: [Int:Range<Int>] = [:]
+
+        var sectionInsertions = self.safeDataSource.dataSourceItems.map { _ in
+          return Set<NSIndexPath>()
+        }
+        var sectionDeletions = self.safeDataSource.dataSourceItems.map { _ in
+          return Set<NSIndexPath>()
+        }
+
+
+        for change in changes {
+          switch change {
+          case .AppendSection:
+            let x = 1
+          case .AppendSections(let numberOfSections): // Number of sections
+            let x = 1
+          case .InsertSection(let sectionIndex): // Section Index
+            let x = 1
+          case .InsertSections(let indexSet):
+            let x = 1
+          case .DeleteSection(let sectionIndex): // Section Index
+            let x = 1
+          case .DeleteSections(let indexSet):
+            let x = 1
+          case .DeleteLastSection:
+            let x = 1
+          case .DeleteSectionsStartingAtIndex(let sectionIndex):
+            let x = 1
+          case .MoveSection(let fromSectionIndex, let toSectionIndex):
+            let x = 1
+          case .AppendItem(_, let sectionIndex): // Item, Section Index
+            if sectionAdditions[sectionIndex] == nil {
+              let startingRange = sectionStartingRanges[sectionIndex]
+              sectionAdditions[sectionIndex] = startingRange.endIndex..<startingRange.endIndex
+            }
+            if let sectionAdditionsForSection = sectionAdditions[sectionIndex] {
+              sectionAdditions[sectionIndex] = sectionAdditionsForSection + 1
+            }
+          case .AppendItems(let items, let sectionIndex): // Items, Section Index
+            if sectionAdditions[sectionIndex] == nil {
+              let startingRange = sectionStartingRanges[sectionIndex]
+              sectionAdditions[sectionIndex] = startingRange.endIndex..<startingRange.endIndex
+            }
+            if let sectionAdditionsForSection = sectionAdditions[sectionIndex] {
+              sectionAdditions[sectionIndex] = sectionAdditionsForSection + items.count
+            }
+          case .InsertItem(_, let indexPath):
+            // TODO: What if this is an insertion into a new section?
+            // A:
+            if sectionStartingRanges[indexPath.section] ~= indexPath.item { // Inserting into existing items
+              sectionInsertions[indexPath.section].insert(indexPath)
+              if let sectionAdditionsForSection = sectionAdditions[indexPath.section] {
+                sectionAdditions[indexPath.section] = sectionAdditionsForSection >> 1
+              }
+            } else { // NOT inserting into existing items
+              if sectionAdditions[indexPath.section] == nil {
+                let startingRange = sectionStartingRanges[indexPath.section]
+                sectionAdditions[indexPath.section] = startingRange.endIndex..<startingRange.endIndex
+              }
+              if let sectionAdditionsForSection = sectionAdditions[indexPath.section] {
+                sectionAdditions[indexPath.section] = sectionAdditionsForSection + 1
+              }
+            }
+            // :A
+
+          case .InsertItems(_, let indexPaths):
+            for indexPath in indexPaths {
+              // A:
+              if sectionStartingRanges[indexPath.section] ~= indexPath.item { // Inserting into existing items
+                sectionInsertions[indexPath.section].insert(indexPath)
+                if let sectionAdditionsForSection = sectionAdditions[indexPath.section] {
+                  sectionAdditions[indexPath.section] = sectionAdditionsForSection >> 1
+                }
+              } else { // NOT inserting into existing items
+                if sectionAdditions[indexPath.section] == nil {
+                  let startingRange = sectionStartingRanges[indexPath.section]
+                  sectionAdditions[indexPath.section] = startingRange.endIndex..<startingRange.endIndex
+                }
+                if let sectionAdditionsForSection = sectionAdditions[indexPath.section] {
+                  sectionAdditions[indexPath.section] = sectionAdditionsForSection + 1
+                }
+              }
+              // :A
+
+            }
+
+          case .DeleteItem(let indexPath):
+            // B:
+            if sectionStartingRanges[indexPath.section] ~= indexPath.item { // Deleting from existing items
+              // This gets complicated, we need to offset any insertions into existing items after this index we are deleting
+              sectionDeletions[indexPath.section].insert(indexPath)
+              for insertionIndexPath in sectionInsertions[indexPath.section] {
+                if insertionIndexPath.item > indexPath.item { // TODO: What if insertion index path matches deletion index path
+                  print("deleting an item that is before item that will be insterted into existing items")
+                  insertionIndexPath.section
+                  insertionIndexPath.item
+                  sectionInsertions[indexPath.section].remove(insertionIndexPath)
+                  sectionInsertions[indexPath.section].insert(NSIndexPath(forItem: insertionIndexPath.item - 1, inSection: insertionIndexPath.section))
+
+                }
+              }
+
+              if let sectionAdditionsForSection = sectionAdditions[indexPath.section] {
+                sectionAdditions[indexPath.section] = sectionAdditionsForSection << 1
+              }
+            } else { // NOT deleting from existing items, no deleting from datasource, items never made it to collection view no need to add these items just to delete them
+              if sectionAdditions[indexPath.section] == nil {
+                let startingRange = sectionStartingRanges[indexPath.section]
+                sectionAdditions[indexPath.section] = startingRange.endIndex..<startingRange.endIndex
+              }
+              if let sectionAdditionsForSection = sectionAdditions[indexPath.section] {
+                sectionAdditions[indexPath.section] = sectionAdditionsForSection - 1 // TODO: Remove addition range if range becomes 0..<0
+              }
+            } // :B
+
+          case .DeleteItems(let indexPaths):
+            for indexPath in indexPaths {
+              // B:
+              if sectionStartingRanges[indexPath.section] ~= indexPath.item { // Deleting from existing items
+                // This gets complicated, we need to offset any insertions into existing items after this index we are deleting
+                sectionDeletions[indexPath.section].insert(indexPath)
+                for insertionIndexPath in sectionInsertions[indexPath.section] {
+                  if insertionIndexPath.item > indexPath.item { // TODO: What if insertion index path matches deletion index path
+                    print("deleting an item that is before item that will be insterted into existing items")
+                    insertionIndexPath.section
+                    insertionIndexPath.item
+                    sectionInsertions[indexPath.section].remove(insertionIndexPath)
+                    sectionInsertions[indexPath.section].insert(NSIndexPath(forItem: insertionIndexPath.item - 1, inSection: insertionIndexPath.section))
+
+                  }
+                }
+
+                if let sectionAdditionsForSection = sectionAdditions[indexPath.section] {
+                  sectionAdditions[indexPath.section] = sectionAdditionsForSection << 1
+                }
+              } else { // NOT deleting from existing items, no deleting from datasource, items never made it to collection view no need to add these items just to delete them
+                if sectionAdditions[indexPath.section] == nil {
+                  let startingRange = sectionStartingRanges[indexPath.section]
+                  sectionAdditions[indexPath.section] = startingRange.endIndex..<startingRange.endIndex
+                }
+                if let sectionAdditionsForSection = sectionAdditions[indexPath.section] {
+                  sectionAdditions[indexPath.section] = sectionAdditionsForSection - 1
+                }
+              } // :B
+            }
+          case .DeleteLastItem(let sectionIndex): // Section Index
+            if let sectionAdditionsForSection = sectionAdditions[sectionIndex] { // There are additions, just remove from there
+              let x = 1
+              sectionAdditions[sectionIndex]?.startIndex
+              sectionAdditions[sectionIndex]?.endIndex
+              sectionAdditions[sectionIndex] = sectionAdditionsForSection - 1
+              if sectionAdditions[sectionIndex]!.startIndex == sectionAdditions[sectionIndex]!.endIndex {
+                sectionAdditions[sectionIndex] = nil
+              }
+            } else if self.safeDataSource.dataSourceItems[sectionIndex].count > 0 { // If there are existing items in this section, remove the last one
+              let x = 1
+              // Look for deletions at the end of the existing items in this section, and a
+              let candidate = NSIndexPath(forItem: self.safeDataSource.dataSourceItems[sectionIndex].count - 1, inSection: sectionIndex)
+              if sectionDeletions[sectionIndex].count == 0 {
+                sectionDeletions[sectionIndex].insert(candidate)
+              } else {
+                var fromEnd = 1
+                var foundDelete = false
+                while !foundDelete {
+                  let candidate = NSIndexPath(forItem: self.safeDataSource.dataSourceItems[sectionIndex].count - fromEnd, inSection: sectionIndex)
+                  if !sectionDeletions[sectionIndex].contains(candidate) {
+                    if sectionInsertions[sectionIndex].contains(candidate) {
+                      sectionInsertions[sectionIndex].remove(candidate)
+                    }
+
+                    sectionDeletions[sectionIndex].insert(candidate)
+                    sectionDeletions[sectionIndex].count // TODO: If this results in a deletion of something that is inside the insertion set of index paths, this logic should remove that index path from the insertions index path
+                    foundDelete = true
+                  } else {
+                    let x = 1
+                    fromEnd += 1
+                  }
+                }
+              }
+
+            } else {
+              print(":-/")
+            }
+
+          case .DeleteItemsStartingAtIndexPath(let indexPath): // Will delete all items from index path forward for section
+            let x = 1
+          case .MoveItem(let fromIndexPath, let toIndexPath):
+            let x = 1
+          }
+        }
+
+
+
+        // TODO: Do this with higher order functions
+        var _newIndexPathsToAdd: [NSIndexPath] = []
+        for (sectionIndex, sa) in sectionAdditions {
+          sa
+          for a in sa {
+            _newIndexPathsToAdd.append(NSIndexPath(forItem: a, inSection: sectionIndex))
+          }
+        }
+
+        for insertions in sectionInsertions {
+          for indexPath in insertions {
+            _newIndexPathsToAdd.append(indexPath)
+          }
+        }
+
+        var _newIndexPathsToDelete: [NSIndexPath] = []
+        for deletions in sectionDeletions {
+          for indexPath in deletions {
+            print("Will Delete: \(indexPath.section):\(indexPath.item)")
+            _newIndexPathsToDelete.append(indexPath)
+          }
+        }
+
+
+
         self.collectionView.performBatchUpdates({
-          if self.collectionView.numberOfSections() == 0 {
-            self.safeDataSource.dataSourceItems.append([])
-            self.collectionView.insertSections(NSIndexSet(index: 0))
+          // Perform changes
+          var indexPathsToAdd: [NSIndexPath] = []
+          for change in changes {
+            if case .AppendSection = change {
+              let newSectionIndex = self.safeDataSource.dataSourceItems.count
+              self.safeDataSource.dataSourceItems.append([])
+              self.collectionView.insertSections(NSIndexSet(index: newSectionIndex))
+            } else if case .InsertItem(let item, let indexPath) = change {
+              print(indexPath.item)
+              indexPathsToAdd.append(indexPath)
+              // TODO: Validate that item's index path matches index in array below:
+              self.safeDataSource.dataSourceItems[indexPath.section].insert(item, atIndex: indexPath.item)
+            } else if case .InsertItems(let items, let newIndexPaths) = change {
+              indexPathsToAdd += newIndexPaths
+              for (index, item) in items.enumerate() {
+                let indexPath = newIndexPaths[index]
+                self.safeDataSource.dataSourceItems[indexPath.section].insert(item, atIndex: indexPath.item)
+              }
+           } else if case .DeleteItem(let indexPath) = change {
+
+              self.safeDataSource.dataSourceItems[indexPath.section].removeAtIndex(indexPath.item)
+
+            } else if case .DeleteLastItem(let sectionIndex) = change {
+              if self.safeDataSource.dataSourceItems[sectionIndex].count > 0 {
+                self.safeDataSource.dataSourceItems[sectionIndex].removeLast()
+              }
+            }
           }
 
 
-          // Perform changes
-          var indexPaths: [NSIndexPath] = []
-          for change in changes {
-            if case .Insert(let item, let indexPath) = change {
-              indexPaths.append(indexPath)
-              // TODO: Validate that item's index path matches index in array below:
-              self.safeDataSource.dataSourceItems[0].append(item)
-            }
+          print("-")
+          if _newIndexPathsToAdd.count > 0 {
+            self.collectionView.insertItemsAtIndexPaths(_newIndexPathsToAdd)
           }
-
-          indexPaths
-          self.collectionView.insertItemsAtIndexPaths(indexPaths)
-//          self.collectionView.deleteItemsAtIndexPaths([])
+          if _newIndexPathsToDelete.count > 0 {
+            self.collectionView.deleteItemsAtIndexPaths(_newIndexPathsToDelete)
+          }
           // Plus reload operations
           // Plus move operations
 
+
         }) { completed in
           self.finishedExecutingOperation()
         }
       }
     }
-
-  }
-
-
-}
-
-class ColorCellProvider: NSObject, SafeCollectionViewDataSource {
-
-  func collectionView<T>(collectionView: UICollectionView, cellForItem item: T, atIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
-    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath)
-    if let color = item as? Color {
-      cell.backgroundColor = UIColor(color: color)
-    }
-    return cell
-  }
-
+  }
+
+
+}
+
+let cellForItem = { (collectionView: UICollectionView, item: Color, indexPath: NSIndexPath) -> UICollectionViewCell in
+  let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath)
+  cell.backgroundColor = UIColor(color: item)
+  return cell
 }
 
 

import XCPlayground






struct Color {
  let hue: CGFloat

collectionView.addGestureRecognizer(tgr)

dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
  for _ in (0...19) {
    interactionHandler.handleTap()
  }

  interactionHandler.safeDataSource.insertItemWithClosure { colors in
    print("Insert item")
    if colors.count > 0 {
      if colors[0].count > 10 {
        colors[0].count - 8
        let indexPath = NSIndexPath(forItem: 4, inSection: 0)
        indexPath.item
        return (Color(hue: 0.1, saturation: 1.0, brightness: 0.9), indexPath)
      }
    }
    return nil
  }

  for _ in (0...1) { // TODO: Try (0...1) and this will crash, support multiple consecutive deletes
    interactionHandler.safeDataSource.deleteItemWithClosure { colors in
      print("Delete item: loop")
      if colors.count > 0 {
        if colors[0].count > 10 {
          return NSIndexPath(forItem: colors[0].count - 2, inSection: 0)
        }
      }
      return nil
    }
  }

  interactionHandler.safeDataSource.deleteItemWithClosure { colors in
    print("Delete item: last")
    return NSIndexPath(forItem: 0, inSection: 0)
  }

  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
  interactionHandler.safeDataSource.deleteLastItemInSection(0)
//  interactionHandler.safeDataSource.deleteLastItemInSection(0) // TODO: uncomment and support this use case

}











