package com.ns.yc.ycstatelib;

import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;

/**
 * <pre>
 *     @author yangchong
 *     blog  : https://github.com/yangchong211
 *     time  : 2017/7/6
 *     desc  : 自定义状态管理,用于list条目中
 *     revise: 借鉴大神齐翔开源库
 *             参考链接:https://github.com/luckybilly/Gloading
 * </pre>
 */
public class StateViewLayout {

    public static final int STATUS_LOADING = 1;
    public static final int STATUS_LOAD_SUCCESS = 2;
    public static final int STATUS_LOAD_FAILED = 3;
    public static final int STATUS_EMPTY_DATA = 4;
    
    private static volatile StateViewLayout mDefault;
    private Adapter mAdapter;
    private static boolean DEBUG = false;

    /**
     * 提供显示当前加载状态的视图
     */
    public interface Adapter {
        /**
         * get view for current status
         * @param holder Holder
         * @param convertView The old view to reuse, if possible.
         * @param status current status
         * @return status view to show. Maybe convertView for reuse.
         * @see Holder
         */
        View getView(Holder holder, View convertView, int status);
    }

    /**
     * set debug mode or not
     * @param debug true:debug mode, false:not debug mode
     */
    public static void debug(boolean debug) {
        DEBUG = debug;
    }

    /**
     * 构造方法私有话,避免外部通过new进行初始化
     */
    private StateViewLayout() { }

    /**
     * Create a new StateViewLayout different from the default one
     * @param adapter another adapter different from the default one
     * @return StateViewLayout
     */
    public static StateViewLayout from(Adapter adapter) {
        checkAdapter(adapter);
        StateViewLayout stateViewLayout = new StateViewLayout();
        stateViewLayout.mAdapter = adapter;
        return stateViewLayout;
    }

    /**
     * get default StateViewLayout object for global usage in whole app
     * @return default StateViewLayout object
     */
    public static StateViewLayout getDefault() {
        if (mDefault == null) {
            synchronized (StateViewLayout.class) {
                if (mDefault == null) {
                    mDefault = new StateViewLayout();
                }
            }
        }
        return mDefault;
    }

    /**
     * init the default loading status view creator ({@link Adapter})
     * @param adapter adapter to create all status views
     */
    public static void initDefault(Adapter adapter) {
        checkAdapter(adapter);
        getDefault().mAdapter = adapter;
    }

    /**
     * StateViewLayout(loading status view) wrap the whole activity
     * wrapper is android.R.id.content
     * @param activity current activity object
     * @return holder of StateViewLayout
     */
    public Holder wrap(Activity activity) {
        ViewGroup wrapper = activity.findViewById(android.R.id.content);
        return new Holder(mAdapter, activity, wrapper);
    }

    /**
     * StateViewLayout(loading status view) wrap the specific view.
     * @param view view to be wrapped
     * @return Holder
     */
    public Holder wrap(View view) {
        FrameLayout wrapper = new FrameLayout(view.getContext());
        ViewGroup.LayoutParams lp = view.getLayoutParams();
        if (lp != null) {
            wrapper.setLayoutParams(lp);
        }
        if (view.getParent() != null) {
            ViewGroup parent = (ViewGroup) view.getParent();
            int index = parent.indexOfChild(view);
            parent.removeView(view);
            parent.addView(wrapper, index);
        }
        LayoutParams newLp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        wrapper.addView(view, newLp);
        return new Holder(mAdapter, view.getContext(), wrapper);
    }

    /**
     * 检查是否设置了自定义adapter,必须先初始化
     * @param adapter                       adapter
     */
    private static void checkAdapter(Adapter adapter){
        if (adapter == null){
            throw new NullPointerException("please set initDefault at first");
        }
    }

    /**
     * StateViewLayout holder<br>
     * create by {@link StateViewLayout#wrap(Activity)} or {@link StateViewLayout#wrap(View)}<br>
     * the core API for showing all status view
     */
    public static class Holder {

        private Adapter mAdapter;
        private Context mContext;
        private Runnable mRetryTask;
        private View mCurStatusView;
        private ViewGroup mWrapper;
        private int curState;
        private SparseArray<View> mStatusViews = new SparseArray<>(4);
        private Object mData;

        private Holder(Adapter adapter, Context context, ViewGroup wrapper) {
            this.mAdapter = adapter;
            this.mContext = context;
            this.mWrapper = wrapper;
        }

        /**
         * set retry task when user click the retry button in load failed page
         * @param task when user click in load failed UI, run this task
         * @return this
         */
        public Holder withRetry(Runnable task) {
            mRetryTask = task;
            return this;
        }

        /**
         * set extension data
         * @param data extension data
         * @return this
         */
        public Holder withData(Object data) {
            this.mData = data;
            return this;
        }

        /** show UI for status: {@link #STATUS_LOADING} */
        public void showLoading() {
            showLoadingStatus(STATUS_LOADING);
        }
        /** show UI for status: {@link #STATUS_LOAD_SUCCESS} */
        public void showLoadSuccess() {
            showLoadingStatus(STATUS_LOAD_SUCCESS);
        }
        /** show UI for status: {@link #STATUS_LOAD_FAILED} */
        public void showLoadFailed() {
            showLoadingStatus(STATUS_LOAD_FAILED);
        }
        /** show UI for status: {@link #STATUS_EMPTY_DATA} */
        public void showEmpty() {
            showLoadingStatus(STATUS_EMPTY_DATA);
        }

        /**
         * Show specific status UI
         * @param status status
         * @see #showLoading()
         * @see #showLoadFailed()
         * @see #showLoadSuccess()
         * @see #showEmpty()
         */
        public void showLoadingStatus(int status) {
            if (curState == status || !validate()) {
                return;
            }
            curState = status;
            //first try to reuse status view
            View convertView = mStatusViews.get(status);
            if (convertView == null) {
                //secondly try to reuse current status view
                convertView = mCurStatusView;
            }
            try {
                //call customer adapter to get UI for specific status. convertView can be reused
                View view = mAdapter.getView(this, convertView, status);
                if (view == null) {
                    printLog(mAdapter.getClass().getName() + ".getView returns null");
                    return;
                }
                if (view != mCurStatusView || mWrapper.indexOfChild(view) < 0) {
                    if (mCurStatusView != null) {
                        mWrapper.removeView(mCurStatusView);
                    }
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        view.setElevation(Float.MAX_VALUE);
                    }
                    mWrapper.addView(view);
                    ViewGroup.LayoutParams lp = view.getLayoutParams();
                    if (lp != null) {
                        lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
                        lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
                    }
                } else if (mWrapper.indexOfChild(view) != mWrapper.getChildCount() - 1) {
                    // 确保加载状态视图在前面
                    view.bringToFront();
                }
                mCurStatusView = view;
                mStatusViews.put(status, view);
            } catch(Exception e) {
                if (DEBUG) {
                    e.printStackTrace();
                }
            }
        }

        private boolean validate() {
            if (mAdapter == null) {
                printLog("StateViewLayout.Adapter is not specified.");
            }
            if (mContext == null) {
                printLog("Context is null.");
            }
            if (mWrapper == null) {
                printLog("The mWrapper of loading status view is null.");
            }
            return mAdapter != null && mContext != null && mWrapper != null;
        }

        public Context getContext() {
            return mContext;
        }

        /**
         * get wrapper
         * @return container of gloading
         */
        public ViewGroup getWrapper() {
            return mWrapper;
        }

        /**
         * get retry task
         * @return retry task
         */
        public Runnable getRetryTask() {
            return mRetryTask;
        }

        /**
         *
         * get extension data
         * @param <T> return type
         * @return data
         */
        public <T> T getData() {
            try {
                return (T) mData;
            } catch(Exception e) {
                if (DEBUG) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }

    private static void printLog(String msg) {
        if (DEBUG) {
            Log.e("StateViewLayout", msg);
        }
    }
}