package tech.starwin.utils.context_utils;

import android.os.Bundle;
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.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 Fragment getCurrentFragment() {
        return currentFragment;
    }


    public FragmentLauncher(FragmentActivity activity, @IdRes int fragmentLayout) {
        this.activity = activity;
        this.fragmentLayout = fragmentLayout;
        framentSet = new HashMap<>();

    }

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

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

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

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

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

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

    /**
     * 显示一个Fragment(没有则先创建)，并隐藏当前的Fragment
     */
    private <T extends Fragment> T changeFragment(Class<T> clz, OnFragmentCreateListener<T> listener, Bundle arguments, boolean isDestory) {
        T fragment = null;
        String className = clz.getName();
        //如果Fragment已被创建，且当前为显示状态，则无需改动
        if (framentSet.containsKey(className)) {
            fragment = (T) framentSet.get(className);
            if (fragment == currentFragment) {
                fragment.setArguments(arguments);
                return fragment;
            }
        }

        FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
        //隐藏当前页
        if (currentFragment != null) {
            if (isDestory) {
                transaction.remove(currentFragment);
                framentSet.remove(currentFragment.getClass().getName());
            } 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;

        if (listener != null) {
            listener.onFragmentCreated(fragment);
        }
        fragment.setArguments(arguments);
        //提交
        transaction.commit();
        return fragment;
    }


    /**
     * 打开一个Fragment
     */
    public int startFragment(Fragment fragment, TransitionConfig config, boolean isAddToBackStack) {
        if (fragmentLayout != 0) {
            String tagName = fragment.getClass().getSimpleName();
            FragmentTransaction transaction = activity.getSupportFragmentManager()
                    .beginTransaction();
            if (config != null) {
                transaction.setCustomAnimations(config.enter, config.exit, config.popenter, config.popout);
            }
            transaction.replace(fragmentLayout, fragment, tagName);
            if (currentFragment != null && framentSet.containsValue(currentFragment)) {
                framentSet.remove(currentFragment.getClass().getName());
                currentFragment = null;
            }
            if (isAddToBackStack)
                transaction.addToBackStack(tagName);
            return transaction.commit();
        }
        return 0;
    }

    /**
     * 打开一个Fragment，且替换BackStack栈顶Fragment
     */
    public int startFragmentAndDestroyCurrent(final Fragment fragment, TransitionConfig config) {
        int index = startFragment(fragment, config, 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;
        }
    }

    public interface OnFragmentCreateListener<T extends Fragment> {
        void onFragmentCreated(T t);
    }

}
