<commit_before>//
//  ThumbnailStorage.swift
//  iAsync_cache
//
//  Created by <NAME> on 22.09.14.
//  Copyright (c) 2014 EmbeddedSources. All rights reserved.
//

import Foundation

import iAsync_network
import iAsync_restkit
import iAsync_utils
import struct iAsync_reactiveKit.AsyncStream
import class iAsync_reactiveKit.StrategyFifo
import class iAsync_reactiveKit.MergedAsyncStream
import class iAsync_reactiveKit.LimitedAsyncStreamsQueue
import enum iAsync_reactiveKit.AsyncEvent
import enum iAsync_reactiveKit.AsyncStreamTypesTransform
import func iAsync_reactiveKit.create
import func iAsync_reactiveKit.asyncStreamWithJob

import ReactiveKit

import UIKit

//alternatives:
//1 - https://github.com/rs/SDWebImage

private let cacheQueueName = "com.embedded_sources.jffcache.thumbnail_storage.cache"

private let noDataUrlStr = "nodata://jff.cache.com"

public extension NSURL {

    public static var noImageDataURL: NSURL {
        struct Static {
            static let instance = NSURL(string: noDataUrlStr)!
        }
        return Static.instance
    }

    public var isNoImageDataURL: Bool {
        return self.absoluteString == noDataUrlStr
    }
}

public var thumbnailStorage = ThumbnailStorage()

final public class ThumbnailStorage {

    private init() {

        NSNotificationCenter.defaultCenter().addObserver(
            self,
            selector: Selector("onMemoryWarning:"),//TODO use #selector
            name: UIApplicationDidReceiveMemoryWarningNotification,
            object: nil)
    }

    private let cachedAsyncOp = MergedAsyncStream<NSURL, UIImage, AnyObject, NSError>()
    private let imagesByUrl   = NSCache()

    public typealias AsyncT = AsyncStream<UIImage, AnyObject, NSError>

    public func thumbnailStreamForUrl(url: NSURL?) -> AsyncT {

        guard let url = url where !url.isNoImageDataURL else { return AsyncT.failed(with: CacheNoURLError()) }

        let stream: AsyncT = create(producer: { observer -> DisposableType? in

            let cachedStream = self.cachedInDBImageDataLoaderForUrl(url)

            let stream = self.cachedAsyncOp.mergedStream({ cachedStream }, key: url, getter: { () -> AsyncEvent<UIImage, AnyObject, NSError>? in
                if let image = self.imagesByUrl.objectForKey(url) as? UIImage {
                    return .Success(image)
                }
                return nil
            }, setter: { event in
                switch event {
                case .Success(let value):
                    self.imagesByUrl.setObject(value, forKey: url)
                default:
                    break
                }<commit_msg>move to swift 2.2<commit_after>//
//  ThumbnailStorage.swift
//  iAsync_cache
//
//  Created by <NAME> on 22.09.14.
//  Copyright (c) 2014 EmbeddedSources. All rights reserved.
//

import Foundation

import iAsync_network
import iAsync_restkit
import iAsync_utils
import struct iAsync_reactiveKit.AsyncStream
import class iAsync_reactiveKit.StrategyFifo
import class iAsync_reactiveKit.MergedAsyncStream
import class iAsync_reactiveKit.LimitedAsyncStreamsQueue
import enum iAsync_reactiveKit.AsyncEvent
import enum iAsync_reactiveKit.AsyncStreamTypesTransform
import func iAsync_reactiveKit.create
import func iAsync_reactiveKit.asyncStreamWithJob

import ReactiveKit

import UIKit

//alternatives:
//1 - https://github.com/rs/SDWebImage

private let cacheQueueName = "com.embedded_sources.jffcache.thumbnail_storage.cache"

private let noDataUrlStr = "nodata://jff.cache.com"

public extension NSURL {

    public static var noImageDataURL: NSURL {
        struct Static {
            static let instance = NSURL(string: noDataUrlStr)!
        }
        return Static.instance
    }

    public var isNoImageDataURL: Bool {
        return self.absoluteString == noDataUrlStr
    }
}

public var thumbnailStorage = ThumbnailStorage()

final public class ThumbnailStorage {

    private init() {

        NSNotificationCenter.defaultCenter().addObserver(
            self,
            selector: #selector(ThumbnailStorage.onMemoryWarning(_:)),
            name    : UIApplicationDidReceiveMemoryWarningNotification,
            object  : nil)
    }

    private let cachedAsyncOp = MergedAsyncStream<NSURL, UIImage, AnyObject, NSError>()
    private let imagesByUrl   = NSCache()

    public typealias AsyncT = AsyncStream<UIImage, AnyObject, NSError>

    public func thumbnailStreamForUrl(url: NSURL?) -> AsyncT {

        guard let url = url where !url.isNoImageDataURL else { return AsyncT.failed(with: CacheNoURLError()) }

        let stream: AsyncT = create(producer: { observer -> DisposableType? in

            let cachedStream = self.cachedInDBImageDataLoaderForUrl(url)

            let stream = self.cachedAsyncOp.mergedStream({ cachedStream }, key: url, getter: { () -> AsyncEvent<UIImage, AnyObject, NSError>? in
                if let image = self.imagesByUrl.objectForKey(url) as? UIImage {
                    return .Success(image)
                }
                return nil
            }, setter: { event in
                switch event {
                case .Success(let value):
                    self.imagesByUrl.setObject(value, forKey: url)
                default:
                    break
                }
{'fraction_non_alphanumeric': 0.07711301044634378, 'fraction_numerical': 0.004558404558404558, 'mean_word_length': 2.821480406386067, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 17, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '652108', 'n_tokens_mistral': 1529, 'n_tokens_neox': 1430, 'n_words': 365}