 import iAsync_utils
 import iAsync_reactiveKit
 
+import ReactiveKit
+
 public enum DataRequestContext<DataLoadContext> {
 
     case Outside(DataLoadContext)


final public class SmartDataLoaderFields<Identifier, Result, DataLoadContext> {

    public typealias JAsyncBinderForIdentifier = (loadDataIdentifier: Identifier) -> AsyncTypes2<(DataRequestContext<DataLoadContext>, NSData), Result, NSError>.AsyncBinder
    public typealias JCacheKeyForIdentifier    = (loadDataIdentifier: Identifier) -> String

    let loadDataIdentifier        : Identifier
    let dataLoaderForIdentifier   : AsyncTypes2<Identifier, (DataLoadContext, NSData), NSError>.AsyncBinder
    let analyzerForData           : JAsyncBinderForIdentifier
    let cacheKeyForIdentifier     : JCacheKeyForIdentifier
    let ignoreFreshDataLoadFail   : Bool
    let cache                     : AsyncRestKitCache
    let cacheDataLifeTimeInSeconds: NSTimeInterval

    public init(
        loadDataIdentifier        : Identifier,
        dataLoaderForIdentifier   : AsyncTypes2<Identifier, (DataLoadContext, NSData), NSError>.AsyncBinder,
        analyzerForData           : JAsyncBinderForIdentifier,
        cacheKeyForIdentifier     : JCacheKeyForIdentifier,
        ignoreFreshDataLoadFail   : Bool,
        cache                     : AsyncRestKitCache,
        cacheDataLifeTimeInSeconds: NSTimeInterval) {

        self.loadDataIdentifier         = loadDataIdentifier
        self.dataLoaderForIdentifier    = dataLoaderForIdentifier
        self.analyzerForData            = analyzerForData
        self.cacheKeyForIdentifier      = cacheKeyForIdentifier
        self.ignoreFreshDataLoadFail    = ignoreFreshDataLoadFail
        self.cache                      = cache
        self.cacheDataLifeTimeInSeconds = cacheDataLifeTimeInSeconds


    let analyzer = { (response: (DataRequestContext<DataLoadContext>, NSData)) -> AsyncTypes<Result, NSError>.Async in

        let analyzer = analyzerForData(response)

        let cacheBinder = { (analyzedData: Result) -> AsyncStream<Result, AnyObject, NSError> in

            let resultLoader: AsyncTypes<Result, NSError>.Async = async(value: analyzedData)

            switch response.0 {
            case .Outside:
                let loader = cache.loaderToSetData(response.1, forKey:cacheKey).toAsync()
                return asyncToStream(sequenceOfAsyncs(loader, resultLoader))
            case .CacheUpdateDate:
                return asyncToStream(resultLoader)
            }
        }

        return analyzer.flatMap(AsyncStreamFlatMapStrategy.Latest, transform: { cacheBinder($0) }).toAsync()
    }

    return bindSequenceOfAsyncs(cachedDataLoader, analyzer)
