 import Foundation
 
 extension ObservableArrayType where Element: Hashable {
-    public typealias ChangeBatch = (deleted: IndexSet, inserted: IndexSet, moved: [(from: Int, to: Int)])
+    public typealias ChangeBatch = (change: Change, deleted: IndexSet, inserted: IndexSet, moved: [(from: Int, to: Int)])
 
     /// Return a source that describes changes in this array in terms of moved elements in addition to insertions and deletions.
     /// The reported changes can be directly fed as batch updates to a `UITableView` or a `UICollectionView`.
-    public func batchedChanges() -> Source<ChangeBatch> {
-        return MoveTrackingArray(inner: self).futureBatches
+    public func batched() -> BatchedArray<Element> {
+        return BatchedArray(inner: self)
     }
 }
 

    }
}

private class MoveTrackingArray<Element: Hashable>: SignalDelegate {
    public typealias ChangeBatch = ObservableArrayType.ChangeBatch

    private let inner: ObservableArray<Element>
    private var _futureBatches = OwningSignal<ChangeBatch, MoveTrackingArray>()

    private var connection: Connection? = nil
    private var value: [Element] = []

    init<Inner: ObservableArrayType>(inner: Inner) where Inner.Element == Element {
        self.inner = inner.observableArray
    }

    var futureBatches: Source<ChangeBatch> {
        return _futureBatches.with(self).source
    }

    func start(_ signal: Signal<ChangeBatch>) {
        precondition(connection == nil)
        value = inner.value
        connection = inner.futureChanges.connect { change in self.process(change) }
    }

    func stop(_ signal: Signal<ChangeBatch>) {
        precondition(connection != nil)
        connection?.disconnect()
        value = []
    }

    private func process(_ change: ArrayChange<Element>) {
        precondition(change.initialCount == value.count)

        let deletedIndices = change.deletedIndices
        var deletedElements: [Element: Int] = [:]
        for i in deletedIndices {
            deletedElements[value[i]] = i
        }

        var insertedElements = change.insertedElements

        var moves: [(from: Int, to: Int)] = []
        for (element, from) in deletedElements {
            if let to = insertedElements[element] {
                moves.append((from, to))

            }
        }

        _value.apply(change)
        precondition(change.finalCount == _value.count)

        let batch: ChangeBatch = (change: change,
                                  deleted: IndexSet(deletedElements.values),
                                  inserted: IndexSet(insertedElements.values),
                                  moved: moves)
        self._futureBatches.send(batch)
    }
}
