/*
 * Created by LuaView.
 * Copyright (c) 2017, Alibaba Group. All rights reserved.
 *
 * This source code is licensed under the MIT.
 * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
 */

package com.taobao.luaview.global;

import com.taobao.luaview.cache.AppCache;
import com.taobao.luaview.fun.base.BaseMethodMapper;
import com.taobao.luaview.fun.binder.constants.AlignBinder;
import com.taobao.luaview.fun.binder.constants.EllipsizeBinder;
import com.taobao.luaview.fun.binder.constants.FontStyleBinder;
import com.taobao.luaview.fun.binder.constants.FontWeightBinder;
import com.taobao.luaview.fun.binder.constants.GravityBinder;
import com.taobao.luaview.fun.binder.constants.InterpolatorBinder;
import com.taobao.luaview.fun.binder.constants.OrientationBinder;
import com.taobao.luaview.fun.binder.constants.PinnedBinder;
import com.taobao.luaview.fun.binder.constants.ScaleTypeBinder;
import com.taobao.luaview.fun.binder.constants.TextAlignBinder;
import com.taobao.luaview.fun.binder.constants.TouchEventBinder;
import com.taobao.luaview.fun.binder.constants.ViewEffectBinder;
import com.taobao.luaview.fun.binder.indicator.UICircleViewPagerIndicatorBinder;
import com.taobao.luaview.fun.binder.kit.ActionBarBinder;
import com.taobao.luaview.fun.binder.kit.DataBinder;
import com.taobao.luaview.fun.binder.kit.FileBinder;
import com.taobao.luaview.fun.binder.kit.JsonBinder;
import com.taobao.luaview.fun.binder.kit.SystemBinder;
import com.taobao.luaview.fun.binder.kit.TimerBinder;
import com.taobao.luaview.fun.binder.ui.SpannableStringBinder;
import com.taobao.luaview.fun.binder.ui.UIAnimatorBinder;
import com.taobao.luaview.fun.binder.ui.UIButtonBinder;
import com.taobao.luaview.fun.binder.ui.UIEditTextBinder;
import com.taobao.luaview.fun.binder.ui.UIHorizontalScrollViewBinder;
import com.taobao.luaview.fun.binder.ui.UIImageViewBinder;
import com.taobao.luaview.fun.binder.ui.UIListViewBinder;
import com.taobao.luaview.fun.binder.ui.UILoadingViewBinder;
import com.taobao.luaview.fun.binder.ui.UIRecyclerViewBinder;
import com.taobao.luaview.fun.binder.ui.UIRefreshLayoutBinder;
import com.taobao.luaview.fun.binder.ui.UIRefreshListViewBinder;
import com.taobao.luaview.fun.binder.ui.UIRefreshRecyclerViewBinder;
import com.taobao.luaview.fun.binder.ui.UIScrollViewBinder;
import com.taobao.luaview.fun.binder.ui.UITextViewBinder;
import com.taobao.luaview.fun.binder.ui.UIToastBinder;
import com.taobao.luaview.fun.binder.ui.UIViewGroupBinder;
import com.taobao.luaview.fun.binder.ui.UIViewPagerBinder;
import com.taobao.luaview.fun.mapper.LuaViewLib;
import com.taobao.luaview.fun.mapper.ui.NewIndexFunction;
import com.taobao.luaview.scriptbundle.asynctask.SimpleTask;
import com.taobao.luaview.scriptbundle.asynctask.SimpleTask0;

import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.LibFunction;
import org.luaj.vm2.lib.jse.JsePlatform;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

/**
 * LuaView Lib管理等
 *
 * @author song
 * @date 15/8/14
 */
public class LuaViewManager {

    public static Globals sStaticGlobals;//使用该全局Globals来加速加载,需要提前创建Globals

    /**
     * pre create globals
     *
     * @return
     */
    public static synchronized void preCreateGlobals() {
        synchronized (LuaViewManager.class) {
            if (sStaticGlobals == null) {
                new SimpleTask0() {
                    @Override
                    public void doTask() {
                        synchronized (LuaViewManager.class) {
                            if (sStaticGlobals == null) {
                                sStaticGlobals = setupGlobals(new Globals());
                            }
                        }
                    }
                }.executeInPool();
            }
        }
    }

    /**
     * 创建Globals
     * 根据是否用lua-to-java bytecode来处理(如果使用LuaJC的话则会使用库bcel 533k)
     *
     * @return
     */
    public static Globals createGlobals() {
        if (sStaticGlobals != null) {
            synchronized (sStaticGlobals) {
                Globals result = sStaticGlobals;
                sStaticGlobals = null;
                return result;
            }
        } else {
            return setupGlobals(new Globals());
        }
    }

    /**
     * 创建Globals
     * 根据是否用lua-to-java bytecode来处理(如果使用LuaJC的话则会使用库bcel 533k)
     *
     * @return
     */
    public static Globals createGlobalsAsync() {
        if (sStaticGlobals != null) {
            synchronized (sStaticGlobals) {
                Globals result = sStaticGlobals;
                sStaticGlobals = null;
                return result;
            }
        } else {
            final Globals globals = new Globals();
            new SimpleTask<Globals>() {
                @Override
                public void doTask(Globals... params) {
                    setupGlobals(params != null && params.length > 0 ? params[0] : null);
                }
            }.executeSerial(globals);//TODO 这里放到serial线程中执行,而不是executeInPool中执行,为了保证后续的执行时序列
            return globals;
        }
    }

    /**
     * setup global values
     *
     * @param globals
     */
    static Globals setupGlobals(Globals globals) {
        if (globals != null) {

            JsePlatform.standardGlobals(globals);//加载系统libs TODO 性能瓶颈

//            if (LuaViewConfig.isUseLuaDC()) {
//                LuaDC.install(globals);
//            }
            loadLuaViewLibs(globals);//加载用户lib TODO 性能瓶颈
            globals.isInited = true;
        }
        return globals;
    }

    /**
     * tryLazyLoad Android API lib
     * TODO 能否做到按需加载,而不是首次进来加载全部binder
     *
     * @param globals
     */
    static void loadLuaViewLibs(final Globals globals) {
        //ui
        globals.tryLazyLoad(new UITextViewBinder());
        globals.tryLazyLoad(new UIEditTextBinder());
        globals.tryLazyLoad(new UIButtonBinder());
        globals.tryLazyLoad(new UIImageViewBinder());
        globals.tryLazyLoad(new UIViewGroupBinder());
        globals.tryLazyLoad(new UIListViewBinder());
        globals.tryLazyLoad(new UIRecyclerViewBinder());
        globals.tryLazyLoad(new UIRefreshListViewBinder());
        globals.tryLazyLoad(new UIRefreshRecyclerViewBinder());
        globals.tryLazyLoad(new UIViewPagerBinder());
        globals.tryLazyLoad(new UICircleViewPagerIndicatorBinder());
        globals.tryLazyLoad(new UIHorizontalScrollViewBinder());
        globals.tryLazyLoad(new UIScrollViewBinder());
        globals.tryLazyLoad(new UILoadingViewBinder());
        globals.tryLazyLoad(new UIToastBinder());
        globals.tryLazyLoad(new SpannableStringBinder());

        globals.tryLazyLoad(new UIRefreshLayoutBinder());

        //animation
        globals.tryLazyLoad(new UIAnimatorBinder());

        //kit
        globals.tryLazyLoad(new TimerBinder());
        globals.tryLazyLoad(new SystemBinder());
        globals.tryLazyLoad(new ActionBarBinder());
        globals.tryLazyLoad(new DataBinder());
        globals.tryLazyLoad(new JsonBinder());
        globals.tryLazyLoad(new FileBinder());

        //常量
        globals.tryLazyLoad(new AlignBinder());
        globals.tryLazyLoad(new TextAlignBinder());
        globals.tryLazyLoad(new FontWeightBinder());
        globals.tryLazyLoad(new FontStyleBinder());
        globals.tryLazyLoad(new ScaleTypeBinder());
        globals.tryLazyLoad(new GravityBinder());
        globals.tryLazyLoad(new OrientationBinder());
        globals.tryLazyLoad(new EllipsizeBinder());
        globals.tryLazyLoad(new InterpolatorBinder());
        globals.tryLazyLoad(new PinnedBinder());
        globals.tryLazyLoad(new ViewEffectBinder());//view特效
        globals.tryLazyLoad(new TouchEventBinder());
    }

    //----------------------------------------bind methods------------------------------------------


    /**
     * bind lua functions using method
     *
     * @param factory
     * @param methods
     * @return
     */
    public static LuaTable bindMethods(Class<? extends LibFunction> factory, List<Method> methods) {
        LuaTable env = new LuaTable();
        try {
            if (methods != null) {
                for (int i = 0; i < methods.size(); i++) {
                    LibFunction f = factory.newInstance();
                    f.opcode = -1;
                    f.method = methods.get(i);
                    f.name = methods.get(i).getName();
                    env.set(f.name, f);
                }
            }
        } catch (Exception e) {
            throw new LuaError("[Bind Failed] " + e);
        } finally {
            return env;
        }
    }

    /**
     * bind lua functions using opcode
     *
     * @param factory
     * @param methods
     * @return
     */
    public static LuaTable bind(Class<? extends LibFunction> factory, List<String> methods) {
        LuaTable env = new LuaTable();
        try {
            if (methods != null) {
                for (int i = 0; i < methods.size(); i++) {
                    LibFunction f = factory.newInstance();
                    f.opcode = i;
                    f.method = null;
                    f.name = methods.get(i);
                    env.set(f.name, f);
                }
            }
        } catch (Exception e) {
            throw new LuaError("[Bind Failed] " + e);
        } finally {
            return env;
        }
    }

    //-----------------------------------------metatable--------------------------------------------

    /**
     * create metatable for libs
     *
     * @return
     */
    public static LuaTable createMetatable(Class<? extends LibFunction> libClass) {
        LuaTable result = AppCache.getCache(AppCache.CACHE_METATABLES).get(libClass);//get from cache

        if (result == null) {
            LuaTable libTable = null;
            if (LuaViewConfig.isUseNoReflection()) {
                List<String> methodNames = getMapperMethodNames(libClass);
                libTable = LuaViewManager.bind(libClass, methodNames);
            } else {
                List<Method> methods = getMapperMethods(libClass);
                libTable = LuaViewManager.bindMethods(libClass, methods);
            }
            result = LuaValue.tableOf(new LuaValue[]{LuaValue.INDEX, libTable, LuaValue.NEWINDEX, new NewIndexFunction(libTable)});

            //update cache
            AppCache.getCache(AppCache.CACHE_METATABLES).put(libClass, result);
        }
        return result;
    }

    /**
     * 获取所有方法的名字
     *
     * @param clazz
     * @return
     */
    static List<String> getMapperMethodNames(Class clazz) {
        try {
            if (clazz != null) {
                Object obj = clazz.newInstance();
                if (obj instanceof BaseMethodMapper) {
                    return ((BaseMethodMapper) obj).getAllFunctionNames();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取所有方法
     *
     * @param clazz
     * @return
     */
    static List<Method> getMapperMethods(Class clazz) {
        List<Method> methods = new ArrayList<Method>();
        getMapperMethodsByClazz(methods, clazz);
        return methods.size() > 0 ? methods : null;
    }

    static void getMapperMethodsByClazz(List<Method> result, Class clazz) {
        if (clazz != null && clazz.isAnnotationPresent(LuaViewLib.class)) {//XXXMapper
            getMapperMethodsByClazz(result, clazz.getSuperclass());//处理super
            Method[] methods = clazz.getDeclaredMethods();
            if (methods != null && methods.length > 0) {
                for (Method method : methods) {//add self
                    if (method.getModifiers() == Modifier.PUBLIC) {//public 方法才行
                        result.add(method);
                    }
                }
            }
        }
    }

}