/*
 * Copyright 2014 Soichiro Kashima
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.ksoichiro.android.observablescrollview;

import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.util.SparseArray;
import android.view.ViewGroup;

/**
 * FragmentStatePagerAdapter that caches each pages.
 * FragmentStatePagerAdapter is also originally caches pages,
 * but its keys are not public nor documented, so depending
 * on how it create cache key is dangerous.
 * This adapter caches pages by itself and provide getter method to the cache.
 */
public abstract class CacheFragmentStatePagerAdapter extends FragmentStatePagerAdapter {

    private static final String STATE_SUPER_STATE = "superState";
    private static final String STATE_PAGES = "pages";
    private static final String STATE_PAGE_KEY_PREFIX = "page:";

    private FragmentManager mFm;
    private SparseArray<Fragment> mPages;

    public CacheFragmentStatePagerAdapter(FragmentManager fm) {
        super(fm);
        mPages = new SparseArray<Fragment>();
        mFm = fm;
    }

    @Override
    public Parcelable saveState() {
        Parcelable p = super.saveState();
        Bundle bundle = new Bundle();
        bundle.putParcelable(STATE_SUPER_STATE, p);

        bundle.putInt(STATE_PAGES, mPages.size());
        if (0 < mPages.size()) {
            for (int i = 0; i < mPages.size(); i++) {
                Fragment f = mPages.get(i);
                mFm.putFragment(bundle, createCacheKey(i), f);
            }
        }
        return bundle;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        Bundle bundle = (Bundle) state;
        int pages = bundle.getInt(STATE_PAGES);
        if (0 < pages) {
            for (int i = 0; i < pages; i++) {
                Fragment f = mFm.getFragment(bundle, createCacheKey(i));
                mPages.put(i, f);
            }
        }

        Parcelable p = bundle.getParcelable(STATE_SUPER_STATE);
        super.restoreState(p, loader);
    }

    /**
     * Get a new Fragment instance.
     * Each fragments are automatically cached in this method,
     * so you don't have to do it by yourself.
     * If you want to implement instantiation of Fragments,
     * you should override {@link #createItem(int)} instead.
     *
     * {@inheritDoc}
     *
     * @param position position of the item in the adapter
     * @return fragment instance
     */
    @Override
    public Fragment getItem(int position) {
        Fragment f = createItem(position);
        // We should cache fragments manually to access to them later
        mPages.put(position, f);
        return f;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        if (0 <= mPages.indexOfKey(position)) {
            mPages.remove(position);
        }
        super.destroyItem(container, position, object);
    }

    /**
     * Get the item at the specified position in the adapter.
     *
     * @param position position of the item in the adapter
     * @return fragment instance
     */
    public Fragment getItemAt(int position) {
        return mPages.get(position);
    }

    /**
     * Create a new Fragment instance.
     * This is called inside {@link #getItem(int)}.
     *
     * @param position position of the item in the adapter
     * @return fragment instance
     */
    protected abstract Fragment createItem(int position);

    /**
     * Create a key string for caching Fragment pages.
     *
     * @param position position of the item in the adapter
     * @return key string for caching Fragment pages
     */
    protected String createCacheKey(int position) {
        return STATE_PAGE_KEY_PREFIX + position;
    }
}

@@ -1436,16 +1436,88 @@
 pages%22;%0A
+    private static final String STATE_PAGE_INDEX_PREFIX = %22pageIndex:%22;%0A
     priv
@@ -1573,16 +1573,16 @@
 page:%22;%0A
-
 %0A    pri
@@ -2117,32 +2117,142 @@
 .size(); i++) %7B%0A
+                int position = mPages.keyAt(i);%0A                bundle.putInt(createCacheIndex(i), position);%0A
                 
@@ -2275,17 +2275,24 @@
 ges.get(
-i
+position
 );%0A     
@@ -2341,17 +2341,24 @@
 acheKey(
-i
+position
 ), f);%0A 
@@ -2644,32 +2644,99 @@
 %3C pages; i++) %7B%0A
+                int position = bundle.getInt(createCacheIndex(i));%0A
                 
@@ -2787,17 +2787,24 @@
 acheKey(
-i
+position
 ));%0A    
@@ -2826,17 +2826,24 @@
 ges.put(
-i
+position
 , f);%0A  
@@ -4331,32 +4331,32 @@
 nstance%0A     */%0A
-
     protected ab
@@ -4394,24 +4394,317 @@
 position);%0A%0A
+    /**%0A     * Create an index string for caching Fragment pages.%0A     *%0A     * @param index index of the item in the adapter%0A     * @return key string for caching Fragment pages%0A     */%0A    protected String createCacheIndex(int index) %7B%0A        return STATE_PAGE_INDEX_PREFIX + index;%0A    %7D%0A%0A
     /**%0A    

Fix that CacheFragmentStatePagerAdapter causes NPE on saving instance state. #50