package com.turlir.abakgists.allgists;

import android.support.annotation.NonNull;

import com.turlir.abakgists.allgists.combination.InlineLoading;
import com.turlir.abakgists.allgists.combination.ListCombination;
import com.turlir.abakgists.allgists.combination.ListManipulator;
import com.turlir.abakgists.allgists.combination.Refresh;
import com.turlir.abakgists.allgists.combination.Start;
import com.turlir.abakgists.model.GistModel;

import io.reactivex.disposables.Disposable;
import io.reactivex.observers.ResourceSingleObserver;
import timber.log.Timber;

class GistLoader {

    private final GistListInteractor mInteractor;
    private final ListManipulator<GistModel> mCallback;

    @NonNull
    private ListCombination<GistModel> mState;
    private Disposable mDatabaseConnection;
    @NonNull
    private String mLastId = "";
    private boolean isEnded;

    GistLoader(GistListInteractor interactor, ListManipulator<GistModel> callback) {
        mInteractor = interactor;
        mCallback = callback;

        mState = new Start(mCallback);
    }

    void firstPage() {
        if (!canLoad()) return;
        mState = new Start(mCallback);
        mState.perform();

        mDatabaseConnection = mInteractor.firstPage()
                .subscribe(gistModels -> {
                    final Range range = mInteractor.range;
                    if (gistModels.size() == 0 && !isEnded) {
                        LoadablePage page = range.page();
                        server(page);
                    }
                    if (gistModels.size() > 0) {
                        changeState(mState.content(gistModels));
                        mLastId = gistModels.get(gistModels.size() - 1).id;
                    }
                    isEnded = range.count() != gistModels.size();

                }, t -> {
                    changeState(mState.error(t));
                });
    }

    void nextPage() {
        if (!canNext()) {
            Timber.d("loading next page is not allowed (is loading or last page reached)");
            return;
        }
        mDatabaseConnection.dispose();
        mDatabaseConnection = mInteractor.nextPage()
                .subscribe(nextItems -> {
                   changeState(mState.content(nextItems));
                    GistModel currentLast = nextItems.get(nextItems.size() - 1);
                    if (currentLast.id.equals(mLastId) && !isEnded) {
                        Range already = mInteractor.range.cut(nextItems.size());
                        Range required = mInteractor.range.diff(already);
                        LoadablePage page = required.page();
                        Timber.d("download required %d th page in %d items", page.number, page.size);
                        server(page);
                        changeState(mState.doLoad());
                    }
                    mLastId = currentLast.id;
                }, t -> {
                    changeState(mState.error(t));
                });
    }

    void prevPage() {
        if (!canPrevious()) {
            Timber.d("loading previous page is not allowed (is loading or first page reached)");
            return;
        }
        mDatabaseConnection.dispose();
        mDatabaseConnection = mInteractor.prevPage()
                .doOnNext(nextItems -> {
                    isEnded = false;
                    mLastId = nextItems.get(nextItems.size() - 1).id;
                })
                .subscribe(nextItems -> {
                    changeState(mState.content(nextItems));
                }, t -> {
                    changeState(mState.error(t));
                });
    }

    int size() {
        return mInteractor.range.prev().absStop;
    }

    boolean isFill(int size) {
        return mInteractor.range.count() == size;
    }

    void stop() {
        if (mDatabaseConnection != null) {
            mDatabaseConnection.dispose();
        }
    }

    private void server(LoadablePage page) {
        mInteractor.server(page)
                .subscribe(new ResourceSingleObserver<Integer>() {
                    @Override
                    public void onSuccess(Integer count) {
                        isEnded = count < page.size;
                        if (isEnded) {
                            Timber.d("gists list ended");
                        }
                        dispose();
                    }
                    @Override
                    public void onError(Throwable e) {
                        changeState(mState.error(e));
                        dispose();
                    }
                });
    }

    private void changeState(ListCombination<GistModel> now) {
        Timber.v("StateChange leave %s, enter %s", mState.getClass().getSimpleName(), now.getClass().getSimpleName());
        mState = now;
        mState.perform();
    }

    private boolean canLoad() {
        return !(mState instanceof InlineLoading) && !(mState instanceof Refresh);
    }

    public boolean canNext() {
        return canLoad() && mInteractor.range.hasNext() && !isEnded;
    }

    public boolean canPrevious() {
        return canLoad() && mInteractor.range.hasPrevious();
    }
}

@@ -2227,35 +2227,56 @@
     
-changeState(mState.content(
+ boolean lessThan = mInteractor.range.count() %3E 
 next
@@ -2272,33 +2272,38 @@
 nt() %3E nextItems
-)
+.size(
 );%0A             
@@ -2319,23 +2319,19 @@
 stModel 
-current
+now
 Last = n
@@ -2390,54 +2390,539 @@
     
-if (currentLast.id.equals(mLastId) && !isEnded
+boolean lastNotChanged = nowLast.id.equals(mLastId);%0A                    mLastId = nowLast.id;%0A                    if (lastNotChanged && !canLoad()) %7B // loading in process%0A                        mState.content(nextItems).perform(); // side effect without state change%0A                        mState.perform(); // repeat loading%0A                    %7D else %7B%0A                        changeState(mState.content(nextItems)); // perform%0A                    %7D%0A                    if (lessThan && lastNotChanged && !isEnded && canNext()
 ) %7B%0A
@@ -3327,24 +3327,24 @@
 .doLoad());%0A
+
             
@@ -3357,54 +3357,8 @@
   %7D%0A
-                    mLastId = currentLast.id;%0A
     

fix bug