package com.tehmou.rxbookapp.data.base.store;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;

import com.tehmou.rxbookapp.data.base.contract.DatabaseContract;

import java.util.HashMap;
import java.util.Map;

import rx.Observable;
import rx.subjects.PublishSubject;
import rx.subjects.Subject;

/**
 * Created by ttuo on 26/04/15.
 */
abstract public class ContentProviderStoreBase<T, U> {
    private static final String TAG = ContentProviderStoreBase.class.getSimpleName();

    final protected ContentResolver contentResolver;
    final private Map<Uri, Subject<T, T>> subjectMap = new HashMap<>();
    final private DatabaseContract<T> databaseContract;

    public ContentProviderStoreBase(ContentResolver contentResolver,
                                    DatabaseContract<T> databaseContract) {
        this.contentResolver = contentResolver;
        this.databaseContract = databaseContract;
        this.contentResolver.registerContentObserver(
                getContentUri(), true, contentObserver);
    }

    final private ContentObserver contentObserver = new ContentObserver(new Handler()) {
        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri);
            Log.v(TAG, "onChange(" + uri + ")");
            if (subjectMap.containsKey(uri)) {
                subjectMap.get(uri).onNext(query(uri));
            }
        }
    };

    public void put(T item) {
        insertOrUpdate(item);
    }

    public Observable<T> getStream(U id) {
        Log.v(TAG, "getStream(" + id + ")");
        final T item = query(id);
        final Observable<T> observable = lazyGetSubject(id);
        if (item != null) {
            Log.v(TAG, "Found existing item for id=" + id);
            return observable.startWith(item);
        }
        return observable;
    }

    private Observable<T> lazyGetSubject(U id) {
        Log.v(TAG, "lazyGetSubject(" + id + ")");
        Uri uri = getUriForKey(id);
        if (!subjectMap.containsKey(uri)) {
            Log.v(TAG, "Creating subject for id=" + id);
            subjectMap.put(uri, PublishSubject.<T>create());
        }
        return subjectMap.get(uri);
    }

    public void insertOrUpdate(T item) {
        Uri uri = getUriForKey(getIdFor(item));
        Log.v(TAG, "insertOrUpdate to " + uri);
        ContentValues values = getContentValuesForItem(item);
        Log.v(TAG, "values(" + values + ")");
        if (contentResolver.update(uri, values, null, null) == 0) {
            final Uri resultUri = contentResolver.insert(uri, values);
            Log.v(TAG, "Inserted at " + resultUri);
        } else {
            Log.v(TAG, "Updated at " + uri);
        }
    }

    protected T query(U id) {
        return query(getUriForKey(id));
    }

    protected T query(Uri uri) {
        Cursor cursor = contentResolver.query(uri,
                databaseContract.getProjection(), null, null, null);
        T value = null;
        if (cursor != null) {
            value = databaseContract.read(cursor);
            cursor.close();
        }
        if (value == null) {
            Log.v(TAG, "Could not find with id: " + uri);
        }
        return value;
    }

    protected ContentValues getContentValuesForItem(T item) {
        return databaseContract.getContentValuesForItem(item);
    }

    public Uri getUriForKey(U id) {
        return Uri.withAppendedPath(getContentUri(), id.toString());
    }

    abstract protected U getIdFor(T item);
    abstract protected Uri getContentUri();
}

@@ -238,24 +238,126 @@
 os.Handler;%0A
+import android.os.HandlerThread;%0Aimport android.os.Looper;%0Aimport android.support.annotation.NonNull;%0A
 import andro
@@ -933,16 +933,90 @@
 ontract;
+%0A    final private ContentObserver contentObserver = getContentObserver();
 %0A%0A    pu
@@ -1370,29 +1370,36 @@
 %0A    %7D%0A%0A    
-final
+@NonNull%0A   
  private Con
@@ -1403,33 +1403,36 @@
 ContentObserver 
-c
+getC
 ontentObserver =
@@ -1429,18 +1429,35 @@
 Observer
- =
+() %7B%0A        return
  new Con
@@ -1473,20 +1473,22 @@
 ver(
-new 
+create
 Handler(
 )) %7B
@@ -1483,21 +1483,56 @@
 Handler(
+this.getClass().getSimpleName()
 )) %7B%0A
+    
         
@@ -1541,16 +1541,20 @@
 verride%0A
+    
         
@@ -1617,16 +1617,20 @@
         
+  
+  
 super.on
@@ -1650,24 +1650,28 @@
 ange, uri);%0A
+    
             
@@ -1699,32 +1699,37 @@
 %22 + uri + %22)%22);%0A
+%0A    
             if (
@@ -1767,32 +1767,36 @@
                 
+    
 subjectMap.get(u
@@ -1831,26 +1831,33 @@
         
-%7D%0A
+    %7D%0A   
         
 %7D%0A    %7D;
@@ -1852,16 +1852,252 @@
     
+ 
 %7D%0A    
+  
+  %7D;%0A    %7D%0A%0A    @NonNull%0A    private static Handler createHandler(String name) %7B%0A        HandlerThread handlerThread = new HandlerThread(name);%0A        handlerThread.start();%0A        return new Handler(handlerThread.getLooper());%0A    
 %7D
-;
 %0A%0A  

Move ContentProviderStoreBase's jobs processing