package co.nano.nanowallet.ui.common;

import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.View;

import co.nano.nanowallet.R;
import co.nano.nanowallet.util.ExceptionHandler;

/**
 * Utility methods for adding and replacing fragment transitions and animations
 */

public class FragmentUtility {
    private FragmentManager mFragmentManager;
    private int mContainerViewId;

    public FragmentUtility(FragmentManager fragmentManager) {
        mFragmentManager = fragmentManager;
    }

    public void addOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener listener) {
        mFragmentManager.addOnBackStackChangedListener(listener);
    }

    public void removeOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener listener) {
        mFragmentManager.removeOnBackStackChangedListener(listener);
    }


    /**
     * Adds a fragment to the container while adding the transaction to the backstack.
     */
    public void add(Fragment fragment) {
        add(fragment, Animation.NONE);
    }

    public void add(Fragment fragment, Animation animations) {
        add(fragment, animations, animations);
    }

    public void add(Fragment fragment, Animation pushAnimations, Animation popAnimations) {
        add(fragment, pushAnimations, popAnimations, null);
    }

    public void add(Fragment fragment,
                    Animation pushAnimations,
                    Animation popAnimations,
                    @Nullable String tag) {
        add(fragment, pushAnimations, popAnimations, tag, null);
    }

    public void add(Fragment fragment,
                    Animation pushAnimations,
                    Animation popAnimations,
                    @Nullable String tag,
                    @Nullable View sharedElement) {
        performAddTransaction(fragment, pushAnimations, popAnimations, tag, true, sharedElement);
    }

    /**
     * Replaces one fragment with another without adding the transaction to the backstack.
     */
    public void replace(Fragment fragment) {
        replace(fragment, Animation.NONE);
    }

    public void replace(Fragment fragment, Animation animations) {
        replace(fragment, animations, animations);
    }

    public void replace(Fragment fragment, Animation pushAnimations, Animation popAnimations) {
        replace(fragment, pushAnimations, popAnimations, null);
    }

    public void replace(Fragment fragment,
                        Animation pushAnimations,
                        Animation popAnimations,
                        @Nullable String tag) {
        performReplaceTransaction(fragment, pushAnimations, popAnimations, tag, false);
    }

    /**
     * Replaces one fragment with another while adding the transaction to the backstack.
     */
    public void push(Fragment fragment) {
        push(fragment, null);
    }

    public void push(Fragment fragment, @Nullable String tag) {
        push(fragment, Animation.NONE, tag);
    }

    public void push(Fragment fragment, Animation animations, @Nullable String tag) {
        push(fragment, animations, animations, tag);
    }

    public void push(Fragment fragment,
                     Animation pushAnimations,
                     Animation popAnimations,
                     @Nullable String tag) {
        replace(fragment, pushAnimations, popAnimations, tag);
    }

    /**
     * Pops the most recent transaction off the backstack.
     */
    public void pop() {
        if (mFragmentManager.getBackStackEntryCount() > 0) {
            mFragmentManager.popBackStack();
        }
    }

    /**
     * Pops the fragment matching the given tag off the backstack.
     */
    public void pop(String tag) {
        Fragment fragment = mFragmentManager.findFragmentByTag(tag);

        if (fragment != null) {
            performRemoveTransaction(fragment);
        }
    }

    /**
     * Pops the given fragment off the backstack.
     */
    public void pop(Fragment fragment) {
        if (fragment != null) {
            performRemoveTransaction(fragment);
        }
    }

    /**
     * Clear the entire stack
     */
    public void clearStack() {
        //Here we are clearing back stack fragment entries
        int backStackEntry = mFragmentManager.getBackStackEntryCount();
        if (backStackEntry > 0) {
            for (int i = 0; i < backStackEntry; i++) {
                try {
                    mFragmentManager.popBackStackImmediate();
                } catch (Exception e) {
                    ExceptionHandler.handle(e);
                }
            }
        }

        //Here we are removing all the fragment that are shown here
        if (mFragmentManager.getFragments() != null && mFragmentManager.getFragments().size() > 0) {
            for (int i = 0; i < mFragmentManager.getFragments().size(); i++) {
                Fragment mFragment = mFragmentManager.getFragments().get(i);
                if (mFragment != null) {
                    try {
                        mFragmentManager.beginTransaction().remove(mFragment).commitAllowingStateLoss();
                    } catch (Exception e) {
                        ExceptionHandler.handle(e);
                    }
                }
            }
        }
    }

    /**
     * Performs a fragment addition transaction with all of the necessary animations and tags.
     */
    public void performAddTransaction(Fragment fragment,
                                      Animation pushAnimations,
                                      Animation popAnimations,
                                      @Nullable String tag,
                                      boolean addToBackStack,
                                      View sharedElement) {
        FragmentTransaction transaction = mFragmentManager.beginTransaction()
                .setCustomAnimations(
                        pushAnimations.getEnter(),
                        popAnimations.getExit(),
                        popAnimations.getEnter(),
                        pushAnimations.getExit()
                )
                .replace(mContainerViewId, fragment, tag);

        if (addToBackStack) {
            transaction.addToBackStack(tag);
        }

        transaction.commitAllowingStateLoss();
    }

    /**
     * Performs a fragment replacement transaction with all of the necessary animations and tags.
     */
    public void performReplaceTransaction(Fragment fragment,
                                          Animation pushAnimations,
                                          Animation popAnimations,
                                          @Nullable String tag,
                                          boolean addToBackStack) {
        FragmentTransaction transaction;
        if (tag == null) {
            transaction = mFragmentManager.beginTransaction()
                    .setCustomAnimations(
                            pushAnimations.getEnter(),
                            popAnimations.getExit(),
                            popAnimations.getEnter(),
                            pushAnimations.getExit()
                    )
                    .replace(mContainerViewId, fragment);
        } else {
            transaction = mFragmentManager.beginTransaction()
                    .setCustomAnimations(
                            pushAnimations.getEnter(),
                            popAnimations.getExit(),
                            popAnimations.getEnter(),
                            pushAnimations.getExit()
                    )
                    .replace(mContainerViewId, fragment, tag);
        }

        if (addToBackStack) {
            transaction.addToBackStack(tag);
        }

        transaction.commitAllowingStateLoss();
    }

    public void performRemoveTransaction(Fragment fragment) {
        mFragmentManager.beginTransaction()
                .remove(fragment)
                .commitAllowingStateLoss();
    }

    public int getBackStackEntryCount() {
        return mFragmentManager.getBackStackEntryCount();
    }

    public FragmentManager getFragmentManager() {
        return mFragmentManager;
    }

    public int getContainerViewId() {
        return mContainerViewId;
    }

    public void setContainerViewId(int containerViewId) {
        this.mContainerViewId = containerViewId;
    }

    public enum Animation {
        NONE(0, 0),
        CROSSFADE(android.R.animator.fade_in, android.R.animator.fade_out),
        ENTER_UP_EXIT_DOWN(R.anim.enter_slide_up, R.anim.exit_slide_down),
        ENTER_LEFT_EXIT_RIGHT(R.anim.enter_slide_left, R.anim.exit_slide_right),
        ENTER_RIGHT_EXIT_LEFT(R.anim.enter_slide_right, R.anim.exit_slide_left);

        final int mEnter;
        final int mExit;

        Animation(int enter, int exit) {
            mEnter = enter;
            mExit = exit;
        }

        public int getEnter() {
            return mEnter;
        }

        public int getExit() {
            return mExit;
        }
    }

    public void cleanUp() {
        mFragmentManager = null;
    }

}