package net.divlight.retainer;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.util.Log;

/**
 * An utility class that helps retaining objects in an Activity / Fragment during configuration
 * changes.
 * <p>
 * Call <code>Retainer.onCreate(this)</code> and <code>Retainer.onDestroy(this)</code> in your
 * activity or fragment. {@link net.divlight.retainer.annotation.Retain} annotated fields will be
 * automatically preserved by Retainer.
 */
public class Retainer {
    private static final String TAG = Retainer.class.getSimpleName();

    private Retainer() {
    }

    /**
     * To preserve {@link net.divlight.retainer.annotation.Retain} annotated fields, Bind a target
     * to a container object(a auto-generated fragment that is called setRetainInstance(true)).
     * <p>
     * This method is intended to be called in {@link FragmentActivity#onCreate(Bundle)}.
     *
     * @param target Target activity for retaining objects.
     */
    public static <T extends FragmentActivity> void onCreate(T target) {
        onCreate(target, target.getSupportFragmentManager(), RetainFragment.DEFAULT_TAG);
    }

    /**
     * To preserve {@link net.divlight.retainer.annotation.Retain} annotated fields, Bind a target
     * to a container object(a auto-generated fragment that is called setRetainInstance(true)).
     * <p>
     * This method is intended to be called in {@link Fragment#onCreate(Bundle)}.
     *
     * @param target Target fragment for retaining objects.
     */
    public static <T extends Fragment> void onCreate(T target) {
        onCreate(target, target.getChildFragmentManager(), RetainFragment.DEFAULT_TAG);
    }

    /**
     * To preserve {@link net.divlight.retainer.annotation.Retain} annotated fields, Bind a target
     * to a container object(a auto-generated fragment that is called setRetainInstance(true)).
     * <p>
     * This method is intended to be called in {@link FragmentActivity#onCreate(Bundle)} or
     * {@link Fragment#onCreate(Bundle)}.
     *
     * @param target          Target activity or fragment for retaining objects.
     * @param fragmentManager FragmentManager used for committing the container fragment.
     * @param tag             The tag name for the container fragment.
     */
    @SuppressWarnings("unchecked")
    public static <T> void onCreate(T target, FragmentManager fragmentManager, String tag) {
        final Fragment fragment = fragmentManager.findFragmentByTag(tag);
        if (fragment != null) {
            final Object<T> object = ((RetainFragment<T>) fragment).getObject();
            if (object != null) {
                object.restore(target);
            }
        } else {
            fragmentManager.beginTransaction()
                    .add(new RetainFragment<T>(), tag)
                    .commitAllowingStateLoss();
        }
    }

    /**
     * Save {@link net.divlight.retainer.annotation.Retain} annotated fields to a container object.
     * <p>
     * This method is intended to be called in {@link FragmentActivity#onDestroy()}.
     *
     * @param target Target activity for retaining objects.
     */
    public static <T extends FragmentActivity> void onDestroy(T target) {
        onDestroy(target, target.getSupportFragmentManager(), RetainFragment.DEFAULT_TAG);
    }

    /**
     * Save {@link net.divlight.retainer.annotation.Retain} annotated fields to a container object.
     * <p>
     * This method is intended to be called in {@link Fragment#onDestroy()}.
     *
     * @param target Target fragment for retaining objects.
     */
    public static <T extends Fragment> void onDestroy(T target) {
        onDestroy(target, target.getChildFragmentManager(), RetainFragment.DEFAULT_TAG);
    }

    /**
     * Save {@link net.divlight.retainer.annotation.Retain} annotated fields to a container object.
     * <p>
     * This method is intended to be called in {@link FragmentActivity#onDestroy()} or
     * {@link Fragment#onDestroy()}.
     *
     * @param target          Target activity or fragment for retaining objects.
     * @param fragmentManager FragmentManager used for finding the container fragment.
     * @param tag             The tag name for the container fragment.
     */
    @SuppressWarnings("unchecked")
    public static <T> void onDestroy(T target, FragmentManager fragmentManager, String tag) {
        final Fragment fragment = fragmentManager.findFragmentByTag(tag);
        if (fragment == null) {
            Log.w(TAG, "Could not find the fragment for preserving instance states."
                    + " Make sure not to forget calling Retainer#onCreate.");
            return;
        }

        try {
            final String packageName = target.getClass().getPackage().getName();
            final String className = getObjectClassName(target.getClass());
            final Class<?> clazz = Class.forName(packageName + "." + className);
            final Object<T> object = (Object<T>) clazz.newInstance();
            object.save(target);
            ((RetainFragment<T>) fragment).setObject(object);
        } catch (Exception e) {
            Log.w(TAG, "Failed to create a object instance for preserving instance states.");
        }
    }

    private static String getObjectClassName(Class<?> targetClass) {
        final String targetClassName = targetClass.getSimpleName();
        final ObjectClassNameBuilder builder = new ObjectClassNameBuilder(targetClassName);
        Class<?> clazz = targetClass.getEnclosingClass();
        while (clazz != null) {
            builder.addEnclosingClassSimpleName(clazz.getSimpleName());
            clazz = clazz.getEnclosingClass();
        }
        return builder.build();
    }
}

@@ -51,16 +51,60 @@
 Bundle;%0A
+import android.support.annotation.UiThread;%0A
 import a
@@ -135,16 +135,16 @@
 agment;%0A
-
 import a
@@ -1110,32 +1110,46 @@
 bjects.%0A     */%0A
+    @UiThread%0A
     public stati
@@ -1665,32 +1665,46 @@
 bjects.%0A     */%0A
+    @UiThread%0A
     public stati
@@ -2479,32 +2479,46 @@
 gs(%22unchecked%22)%0A
+    @UiThread%0A
     public stati
@@ -3324,32 +3324,46 @@
 bjects.%0A     */%0A
+    @UiThread%0A
     public stati
@@ -3785,24 +3785,38 @@
 ts.%0A     */%0A
+    @UiThread%0A
     public s
@@ -4454,32 +4454,32 @@
 agment.%0A     */%0A
-
     @SuppressWar
@@ -4493,24 +4493,38 @@
 unchecked%22)%0A
+    @UiThread%0A
     public s

Add @UiThread annotations