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

public func jSmartDataLoaderWithCache<Identifier, Result, DataLoadContext>(args: SmartDataLoaderFields<Identifier, Result, DataLoadContext>) -> AsyncTypes<Result, NSError>.Async {

    let loadDataIdentifier         = args.loadDataIdentifier
    let dataLoaderForIdentifier    = args.dataLoaderForIdentifier
    let analyzerForData            = args.analyzerForData
    let cache                      = args.cache
    let cacheKeyForIdentifier      = args.cacheKeyForIdentifier
    let cacheDataLifeTimeInSeconds = args.cacheDataLifeTimeInSeconds
    let ignoreFreshDataLoadFail    = args.ignoreFreshDataLoadFail

    let key = cacheKeyForIdentifier(loadDataIdentifier: loadDataIdentifier)

    let cachedDataLoader = { (progressCallback: AsyncProgressCallback?,
                              finishCallback  : AsyncTypes<(DataRequestContext<DataLoadContext>, NSData), NSError>.DidFinishAsyncCallback?) -> AsyncHandler in

        let loadCachedData: AsyncTypes<(DataRequestContext<DataLoadContext>, NSData), NSError>.Async = loadFreshCachedDataWithUpdateDate(
            key,
            cachedDataLoader          : cache.cachedDataLoaderForKey(key),
            cacheDataLifeTimeInSeconds: cacheDataLifeTimeInSeconds)

        let dataLoaderBinder = dataLoaderWithCachedResultBinder(
            ignoreFreshDataLoadFail,
            dataLoaderForIdentifier: dataLoaderForIdentifier,
            loadDataIdentifier     : loadDataIdentifier)

        let loader = bindTrySequenceOfAsyncs(loadCachedData, dataLoaderBinder)



private func dataLoaderWithCachedResultBinder<Identifier, DataLoadContext>(
    ignoreFreshDataLoadFail: Bool,
    dataLoader             : AsyncStream<(DataLoadContext, NSData), AnyObject, NSError>,
    loadDataIdentifier     : Identifier) -> AsyncTypes2<NSError, (DataRequestContext<DataLoadContext>, NSData), NSError>.AsyncBinder
{
    return { (bindError: NSError) -> AsyncTypes<(DataRequestContext<DataLoadContext>, NSData), NSError>.Async in

        let dataLoader = dataLoader.map({ value -> (DataRequestContext<DataLoadContext>, NSData) in

            let newResult = (DataRequestContext<DataLoadContext>.Outside(value.0), value.1)
            return newResult
        }).tryMapError({ error -> Result<(DataRequestContext<DataLoadContext>, NSData), NSError> in

            if error is AsyncInterruptedError {
                return .Failure(error)
            }

            //TODO test [bindError isKindOfClass:[JFFErrorNoFreshData class]] issue, here it can got - not data in cache error !!!
            if ignoreFreshDataLoadFail {
                if let noFreshDataError = bindError as? ErrorNoFreshData {

                    let newResult = (DataRequestContext<DataLoadContext>.CacheUpdateDate(noFreshDataError.cachedData.0), noFreshDataError.cachedData.1)

                    return .Success(newResult)
                }
            }

            return .Failure(error)
        })

        return dataLoader.toAsync()
    }
}

