package tech.starwin.utils.context_utils;

import android.support.annotation.IdRes;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import tech.starwin.R;
import tech.starwin.utils.GeneralUtils;


/**
 * Created by SiKang on 2018/9/20.
 * Fragment的启动管理
 */
public class FragmentLauncher {
    private FragmentActivity activity;

    /**
     * 绑定的Layout
     */
    private int fragmentLayout;

    /**
     * 当前展示的Fragment
     */
    private Fragment currentFragment;


    /**
     * Fragment缓存
     */
    private Map<String, Fragment> framentSet;


    public FragmentLauncher(FragmentActivity activity, @IdRes int fragmentLayout) {
        this.activity = activity;
        this.fragmentLayout = fragmentLayout;

    }

    public <T extends Fragment> T showFragmentAndHideCurrent(Class<T> clz) {
        return changeFragment(clz, false);
    }

    public <T extends Fragment> T showFragmentAndDestoryCurrent(Class<T> clz) {
        return changeFragment(clz, true);
    }

    /**
     * 显示一个Fragment(没有则先创建)，并隐藏当前的Fragment
     */
    private <T extends Fragment> T changeFragment(Class<T> clz, boolean isDestory) {
        if (framentSet == null)
            framentSet = new HashMap<>();

        T fragment = null;
        String className = clz.getName();
        //如果Fragment已被创建，且当前为显示状态，则无需改动
        if (framentSet.containsKey(className)) {
            fragment = (T) framentSet.get(className);
            if (fragment == currentFragment) {
                return fragment;
            }
        }

        FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
        //隐藏当前页
        if (currentFragment != null) {
            if (isDestory) {
                transaction.remove(currentFragment);
            } else {
                transaction.hide(currentFragment);
            }
        }
        //如果Fragment还未被创建，则新建后添加到FragmentManager
        if (fragment == null) {
            fragment = (T) T.instantiate(activity, className);
            framentSet.put(className, fragment);
            transaction.add(fragmentLayout, fragment);
        } else {
            //如果Fragment已被创建，取出缓存显示
            transaction.show(fragment);
        }
        //更新当前页
        currentFragment = fragment;
        //提交
        transaction.commit();
        return fragment;
    }


    /**
     * 打开一个Fragment
     * 必须实现 bindFragmentLayout() 绑定FrameLayout
     */
    public int startFragment(Fragment fragment, boolean isAddToBackStack) {
        if (fragmentLayout != 0) {
            TransitionConfig config = new TransitionConfig(
                    R.anim.slide_in_right, R.anim.slide_out_left,
                    R.anim.slide_in_left, R.anim.slide_out_right);
            String tagName = fragment.getClass().getSimpleName();
            FragmentTransaction transaction = activity.getSupportFragmentManager()
                    .beginTransaction()
                    .setCustomAnimations(config.enter, config.exit, config.popenter, config.popout)
                    .replace(fragmentLayout, fragment, tagName);

            if (isAddToBackStack)
                transaction.addToBackStack(tagName);
            return transaction.commit();
        }
        return 0;
    }

    /**
     * 打开一个Fragment，且替换BackStack栈顶Fragment
     * 必须实现 bindFragmentLayout() 绑定FrameLayout
     */
    public int startFragmentAndDestroyCurrent(final Fragment fragment) {
        int index = startFragment(fragment, false);
        GeneralUtils.findAndModifyOpInBackStackRecord(activity.getSupportFragmentManager(), -1, new GeneralUtils.OPHandler() {
            @Override
            public boolean handle(Object op) {
                Field cmdField = null;
                try {
                    cmdField = op.getClass().getDeclaredField("cmd");
                    cmdField.setAccessible(true);
                    int cmd = (int) cmdField.get(op);
                    if (cmd == 1) {
                        Field oldFragmentField = op.getClass().getDeclaredField("fragment");
                        oldFragmentField.setAccessible(true);
                        Object fragmentObj = oldFragmentField.get(op);
                        oldFragmentField.set(op, fragment);
                        Field backStackNestField = Fragment.class.getDeclaredField("mBackStackNesting");
                        backStackNestField.setAccessible(true);
                        int oldFragmentBackStackNest = (int) backStackNestField.get(fragmentObj);
                        backStackNestField.set(fragment, oldFragmentBackStackNest);
                        backStackNestField.set(fragmentObj, --oldFragmentBackStackNest);
                        return true;
                    }
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                return false;
            }
        });
        return index;
    }

    public static final class TransitionConfig {
        public final int enter;
        public final int exit;
        public final int popenter;
        public final int popout;

        public TransitionConfig(int enter, int popout) {
            this(enter, 0, 0, popout);
        }

        public TransitionConfig(int enter, int exit, int popenter, int popout) {
            this.enter = enter;
            this.exit = exit;
            this.popenter = popenter;
            this.popout = popout;
        }
    }

}
