package mlxy.tumplar.model;

import android.net.Uri;

import com.squareup.okhttp.HttpUrl;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.inject.Inject;

import mlxy.tumplar.global.App;
import mlxy.tumplar.model.service.AvatarService;
import retrofit.Response;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

public class AvatarModel {
    private static class Instance {
        private static final AvatarModel instance = new AvatarModel();
    }

    @Inject
    AvatarService service;

    private static ConcurrentHashMap<String, Observable<Uri>> observableCache;
    private static ConcurrentHashMap<String, Uri> cache;

    private AvatarModel() {
        App.graph.inject(this);
        observableCache = new ConcurrentHashMap<>();
        cache = new ConcurrentHashMap<>();
    }

    public static AvatarModel getInstance() {
        return Instance.instance;
    }

    public Observable<Uri> get(final String blogName) {
        Observable<Uri> cachedObservable = observableCache.get(blogName);
        if (cachedObservable != null) {
            return cachedObservable;
        }

        Observable<Uri> uriObservable = Observable.concat(fromCache(blogName), fromNet(blogName))
                .first(new Func1<Uri, Boolean>() {
                    @Override
                    public Boolean call(Uri uri) {
                        return uri != null;
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnTerminate(new Action0() {
                    @Override
                    public void call() {
                        observableCache.remove(blogName);
                    }
                });

        // XXX only cache task fromNet for better performance
        Observable<Uri> doubleCheck = observableCache.putIfAbsent(blogName, uriObservable);
        if (doubleCheck != null) {
            return doubleCheck;
        }

        return uriObservable;
    }

    private Observable<Uri> fromCache(final String blogName) {
        return Observable.create(new Observable.OnSubscribe<Uri>() {
            @Override
            public void call(Subscriber<? super Uri> subscriber) {
                subscriber.onNext(cache.get(blogName));
                subscriber.onCompleted();
            }
        });
    }

    private Observable<Uri> fromNet(final String blogName) {
        return service.avatar(blogName)
                .flatMap(new Func1<Response, Observable<Uri>>() {
                    @Override
                    public Observable<Uri> call(Response response) {
                        HttpUrl avatarUrl = response.raw().request().httpUrl();
                        return Observable.just(Uri.parse(avatarUrl.toString()));
                    }
                }).doOnNext(new Action1<Uri>() {
                    @Override
                    public void call(Uri uri) {
                        cache.putIfAbsent(blogName, uri);
                    }
                });
    }
}

@@ -1117,32 +1117,125 @@
 ing blogName) %7B%0A
+        if (cache.containsKey(blogName)) %7B%0A            return fromCache(blogName);%0A        %7D%0A
         Observab
@@ -1392,39 +1392,33 @@
     
-Observable%3CUri%3E uriObservable =
+// XXX try something like
  Obs
@@ -1430,19 +1430,8 @@
 le.c
-oncat(fromC
 ache
@@ -1444,244 +1444,65 @@
 ame)
-, fromNet(blogName))%0A                .first(new Func1%3CUri, Boolean%3E() %7B%0A                    @Override%0A                    public Boolean call(Uri uri) %7B%0A                        return uri != null;%0A                    %7D%0A                %7D
+?%0A        Observable%3CUri%3E uriObservable =fromNet(blogName
 )%0A  
@@ -1827,70 +1827,8 @@
 );%0A%0A
-        // XXX only cache task fromNet for better performance%0A
     

Improve performance of AvatarModel