content: /*
   * 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.util;
  
  import com.taobao.luaview.userdata.kit.UDUnicode;
  import com.taobao.luaview.userdata.ui.UDSpannableString;
  
  import org.luaj.vm2.LuaFunction;
  import org.luaj.vm2.LuaNumber;
  import org.luaj.vm2.LuaTable;
  import org.luaj.vm2.LuaUserdata;
  import org.luaj.vm2.LuaValue;
  import org.luaj.vm2.Varargs;
  import org.luaj.vm2.lib.jse.CoerceJavaToLua;
  
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  /**
   * Lua脚本工具类
   *
   * @author song
   * @date 15/9/14
   */
  public class LuaUtil {
  
  
      //-------------------------------------get value------------------------------------------------
  
      /**
       * 获取alpha
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static Integer getAlphaInt(final Varargs varargs, int... poslist) {
          return getAlphaInt(varargs, null, poslist);
      }
  
      public static Integer getAlphaInt(final Varargs varargs, Double defaultAlpha, int... poslist) {
          final Double alphaDouble = getDouble(varargs, poslist);
          if (alphaDouble != null) {
              return (int) (alphaDouble * 0xFF);
          } else if (defaultAlpha != null) {
              return (int) (defaultAlpha * 0xFF);
          } else {
              return null;
          }
      }
  
      /**
       * convert a LuaValue to Alpha int
       * @param value
       * @return
       */
      public static Integer toAlphaInt(final LuaValue value) {
          if (isNumber(value)) {
              final double dValue = value.optdouble(-1);
              if (dValue >= 0) {
                  return (int) (dValue * 0xFF);
              }
          }
          return null;
      }
  
      /**
       * 获取时间，入参为秒(可以是小数)，输出为毫秒
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static Long getTimeLong(final Varargs varargs, int... poslist) {
          return getTimeLong(varargs, null, poslist);
      }
  
      /**
       * 获取时间，入参为秒(可以是小数)，输出为毫秒
       *
       * @param varargs
       * @param defaultSeconds
       * @param poslist
       * @return
       */
      public static Long getTimeLong(final Varargs varargs, Float defaultSeconds, int... poslist) {
          final Float timeOfFloat = getFloat(varargs, poslist);
          if (timeOfFloat != null) {
              return (long) (timeOfFloat * 1000);
          } else if (defaultSeconds != null) {
              return (long) (defaultSeconds * 1000);
          } else {
              return null;
          }
      }
  
  
      /**
       * 获取boolean
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static Boolean getBoolean(final Varargs varargs, int... poslist) {
          return (Boolean) getValue(LuaValue.TBOOLEAN, varargs, poslist);
      }
  
      public static Boolean getBoolean(final Varargs varargs, Boolean defaultValue, int... poslist) {
          return (Boolean) getValue(LuaValue.TBOOLEAN, varargs, defaultValue, poslist);
      }
  
      /**
       * 获取int
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static Integer getInt(final Varargs varargs, int... poslist) {
          final LuaNumber number = (LuaNumber) getValue(LuaValue.TNUMBER, varargs, poslist);
          return number != null ? number.checkint() : null;
      }
  
      /**
       * 获取long
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static Long getLong(final Varargs varargs, int... poslist) {
          final LuaNumber number = (LuaNumber) getValue(LuaValue.TNUMBER, varargs, poslist);
          return number != null ? number.checklong() : null;
      }
  
      public static Long getLong(final Varargs varargs, Long defaultValue, int... poslist) {
          final LuaNumber number = (LuaNumber) getValue(LuaValue.TNUMBER, varargs, defaultValue, poslist);
          return number != null ? number.checklong() : null;
      }
  
      /**
       * 获取double
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static Double getDouble(final Varargs varargs, int... poslist) {
          final LuaNumber number = (LuaNumber) getValue(LuaValue.TNUMBER, varargs, poslist);
          return number != null ? number.checkdouble() : null;
      }
  
      /**
       * 获取double
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static Double getDouble(final Varargs varargs, Double defaultValue, int... poslist) {
          final LuaNumber number = (LuaNumber) getValue(LuaValue.TNUMBER, varargs, poslist);
          return number != null ? number.checkdouble() : defaultValue;
      }
  
      /**
       * 获取float
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static Float getFloat(final Varargs varargs, int... poslist) {
          final LuaNumber number = (LuaNumber) getValue(LuaValue.TNUMBER, varargs, poslist);
          return number != null ? (float) number.checkdouble() : null;
      }
  
      /**
       * 获取float
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static Float getFloat(final Varargs varargs, Float defaultValue, int... poslist) {
          final LuaNumber number = (LuaNumber) getValue(LuaValue.TNUMBER, varargs, defaultValue, poslist);
          return number != null ? (float) number.checkdouble() : defaultValue;
      }
  
  
      /**
       * 获取string
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static String getString(final Varargs varargs, int... poslist) {
          return (String) getValue(LuaValue.TSTRING, varargs, poslist);
      }
  
      public static String getString(final LuaValue valueList, String... poslist) {
          return (String) getValueFromTable(LuaValue.TSTRING, valueList, poslist);
      }
  
      /**
       * 获取char sequence
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static CharSequence getText(final Varargs varargs, int... poslist) {
          LuaValue value = getValue(varargs, poslist);
          return toText(value);
      }
  
      /**
       * 获取char sequence
       *
       * @param values
       * @param poslist
       * @return
       */
      public static CharSequence getText(final LuaValue values, String... poslist) {
          LuaValue value = (LuaValue) getValueFromTable(LuaValue.TVALUE, values, poslist);
          return toText(value);
      }
  
      /**
       * 获取table
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static LuaTable getTable(final Varargs varargs, int... poslist) {
          return (LuaTable) getValue(LuaValue.TTABLE, varargs, poslist);
      }
  
      public static LuaTable getTable(final LuaValue valuelist, String... poslist) {
          return (LuaTable) getValueFromTable(LuaValue.TTABLE, valuelist, poslist);
      }
  
      /**
       * 获取function
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static LuaFunction getFunction(final Varargs varargs, int... poslist) {
          return (LuaFunction) getValue(LuaValue.TFUNCTION, varargs, poslist);
      }
  
      public static LuaFunction getFunction(final LuaValue valueList, String... keylist) {
          return (LuaFunction) getValueFromTable(LuaValue.TFUNCTION, valueList, keylist);
      }
  
  
      /**
       * 获取LuaValue
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static LuaValue getValue(final Varargs varargs, int... poslist) {
          return (LuaValue) getValue(LuaValue.TVALUE, varargs, poslist);
      }
  
      public static LuaValue getValue(final Varargs varargs, String... poslist) {
          return (LuaValue) getValueFromTable(LuaValue.TVALUE, varargs, poslist);
      }
  
      public static LuaValue getValue(final Varargs varargs, Varargs defaultValue, String... poslist) {
          return (LuaValue) getValueFromTable(LuaValue.TVALUE, varargs, defaultValue, poslist);
      }
  
      /**
       * 获取LuaValue by poslist name
       *
       * @param valuelist
       * @param poslist
       * @return
       */
      public static LuaValue getValue(final LuaValue valuelist, String... poslist) {
          return (LuaValue) getValueFromTable(LuaValue.TVALUE, valuelist, poslist);
      }
  
      public static LuaValue getValue(final LuaValue valuelist, LuaValue defaultValue, String... poslist) {
          return (LuaValue) getValueFromTable(LuaValue.TVALUE, defaultValue, valuelist, poslist);
      }
  
      /**
       * 获取userdata
       *
       * @param varargs
       * @param poslist
       * @return
       */
      public static LuaUserdata getUserdata(final Varargs varargs, int... poslist) {
          return (LuaUserdata) getValue(LuaValue.TUSERDATA, varargs, poslist);
      }
  
      /**
       * get value of given type, from varargs in position [poslist]
       *
       * @param type
       * @param varargs
       * @param poslist
       * @return
       */
      private static Object getValue(final int type, final Varargs varargs, int... poslist) {
          return getValue(type, varargs, null, poslist);
      }
  
      /**
       * get value of given type, from varargs in position [poslist]
       *
       * @param type
       * @param varargs
       * @param poslist
       * @return
       */
      private static Object getValue(final int type, final Varargs varargs, Object defaultValue, int... poslist) {
          Object result = null;
          if (varargs != null) {
              if (poslist != null && poslist.length > 0) {
                  LuaValue value = null;
                  for (int i = 0; i < poslist.length; i++) {
                      if (varargs.narg() >= poslist[i]) {
                          value = varargs.arg(poslist[i]);
                          result = parseValue(type, value);
                      }
                      if (result != null) {
                          break;
                      }
                  }
              }
          }
          return result != null ? result : defaultValue;
      }
  
  
      /**
       * get value from table
       *
       * @param type
       * @param valueList
       * @param keylist
       * @return
       */
      private static Object getValueFromTable(final int type, final Varargs valueList, String... keylist) {
          return getValueFromTable(type, valueList, null, keylist);
      }
  
      /**
       * get value of given type, from varargs of key [poslist]
       *
       * @param type
       * @param varargs
       * @param keylist
       * @return
       */
      private static Object getValueFromTable(final int type, final Varargs varargs, Object defaultValue, String... keylist) {
          Object result = null;
          if (varargs instanceof LuaTable) {
              LuaTable varlist = ((LuaTable) varargs);
              if (keylist != null && keylist.length > 0) {
                  for (int i = 0; i < keylist.length; i++) {
                      result = parseValue(type, varlist.get(keylist[i]));
                      if (result != null) {
                          break;
                      }
                  }
              }
          }
          return result != null ? result : defaultValue;
      }
  
      /**
       * parse a value to given type
       *
       * @param type
       * @param value
       * @return
       */
      private static Object parseValue(int type, LuaValue value) {
          switch (type) {
              case LuaValue.TBOOLEAN:
                  if (isBoolean(value)) return value.checkboolean();
                  break;
              case LuaValue.TNUMBER:
                  if (isNumber(value)) return value.checknumber();
                  break;
              case LuaValue.TSTRING:
                  if (isString(value)) return value.checkjstring();
                  break;
              case LuaValue.TTABLE:
                  if (isTable(value)) return value.checktable();
                  break;
              case LuaValue.TFUNCTION:
                  if (isFunction(value)) return value.checkfunction();
                  break;
              case LuaValue.TUSERDATA:
                  if (isUserdata(value)) return value.checkuserdata();
                  break;
              case LuaValue.TVALUE:
                  return value;
          }
          return null;
      }
      //---------------------------------type of value------------------------------------------------
  
      /**
       * is string
       *
       * @param target
       * @return
       */
      public static boolean isString(final LuaValue target) {
          return target != null && target.type() == LuaValue.TSTRING;
      }
  
      /**
       * is number
       *
       * @param target
       * @return
       */
      public static boolean isNumber(final LuaValue target) {
          return target != null && target.type() == LuaValue.TNUMBER;
      }
  
      /**
       * is boolean
       *
       * @param target
       * @return
       */
      public static boolean isBoolean(final LuaValue target) {
          return target != null && target.type() == LuaValue.TBOOLEAN;
      }
  
      /**
       * is function
       *
       * @param target
       * @return
       */
      public static boolean isFunction(final LuaValue target) {
          return target != null && target.type() == LuaValue.TFUNCTION;
      }
  
      /**
       * is table
       *
       * @param target
       * @return
       */
      public static boolean isTable(final LuaValue target) {
          return target != null && target.type() == LuaValue.TTABLE;
      }
  
      /**
       * is userdata
       *
       * @param target
       * @return
       */
      public static boolean isUserdata(final LuaValue target) {
          return target != null && target.type() == LuaValue.TUSERDATA;
      }
  
      /**
       * is nil
       *
       * @param target
       * @return
       */
      public static boolean isNil(final LuaValue target) {
          return target != null && target.type() == LuaValue.TNIL;
      }
  
      /**
       * is none
       *
       * @param target
       * @return
       */
      public static boolean isNone(final LuaValue target) {
          return target != null && target.type() == LuaValue.TNONE;
      }
  
      /**
       * 判断是否空或者nil
       *
       * @param target
       * @return
       */
      public static boolean isValid(final LuaValue target) {
          return target != null && target.type() != LuaValue.TNIL;
      }
  
      //------------------------------------function call---------------------------------------------
  
      /**
       * call lua function
       *
       * @param target
       * @return
       */
      public static LuaValue callFunction(LuaValue target) {
          try {
              return (target != null && target.isfunction()) ? target.call() : LuaValue.NIL;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      public static LuaValue callFunction(LuaValue target, LuaValue arg1) {
          try {
              return (target != null && target.isfunction()) ? target.call(arg1) : LuaValue.NIL;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      public static LuaValue callFunction(LuaValue target, LuaValue arg1, LuaValue arg2) {
          try {
              return (target != null && target.isfunction()) ? target.call(arg1, arg2) : LuaValue.NIL;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      public static LuaValue callFunction(LuaValue target, LuaValue arg1, LuaValue arg2, LuaValue arg3) {
          try {
              return (target != null && target.isfunction()) ? target.call(arg1, arg2, arg3) : LuaValue.NIL;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      public static Varargs callFunction(LuaValue target, Object... objs) {
          if (target != null && target.isfunction()) {
              LuaValue[] args = null;
              if (objs != null && objs.length > 0) {
                  args = new LuaValue[objs.length];
  
                  for (int i = 0; i < objs.length; i++) {
                      args[i] = CoerceJavaToLua.coerce(objs[i]);
                  }
              }
              if (args != null) {
                  return target.invoke(LuaValue.varargsOf(args));
              } else {
                  return target.call();
              }
          }
          return LuaValue.NIL;
      }
  
      /**
       * call lua function ( return multiple values)
       *
       * @param target
       * @return
       */
      public static Varargs invokeFunction(LuaValue target) {
          try {
              return (target != null && target.isfunction()) ? target.invoke() : LuaValue.NIL;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      public static Varargs invokeFunction(LuaValue target, LuaValue arg1) {
          try {
              return (target != null && target.isfunction()) ? target.invoke(arg1) : LuaValue.NIL;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      public static Varargs invokeFunction(LuaValue target, LuaValue arg1, LuaValue arg2) {
          try {
              return (target != null && target.isfunction()) ? target.invoke(arg1, arg2) : LuaValue.NIL;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      public static Varargs invokeFunction(LuaValue target, LuaValue arg1, LuaValue arg2, LuaValue arg3) {
          try {
              return (target != null && target.isfunction()) ? target.invoke(new LuaValue[]{arg1, arg2, arg3}) : LuaValue.NIL;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      /**
       * 调用方法或者直接取数据
       *
       * @param target
       * @return
       */
      public static LuaValue getOrCallFunction(LuaValue target) {
          try {
              return (target != null && target.isfunction()) ? target.call() : target;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      public static LuaValue getOrCallFunction(LuaValue target, LuaValue arg1) {
          try {
              return (target != null && target.isfunction()) ? target.call(arg1) : target;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      public static LuaValue getOrCallFunction(LuaValue target, LuaValue arg1, LuaValue arg2) {
          try {
              return (target != null && target.isfunction()) ? target.call(arg1, arg2) : target;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      public static LuaValue getOrCallFunction(LuaValue target, LuaValue arg1, LuaValue arg2, LuaValue arg3) {
          try {
              return (target != null && target.isfunction()) ? target.call(arg1, arg2, arg3) : target;
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      /**
       * lua 都是从1开始
       *
       * @param pos
       * @return
       */
      public static LuaValue toLuaInt(Integer pos) {
          return pos != null ? LuaValue.valueOf(pos + 1) : LuaValue.NIL;
      }
  
      public static LuaValue toLuaBoolean(boolean value) {
          return LuaValue.valueOf(value);
      }
  
      /**
       * java 从 0 开始
       *
       * @param pos
       * @return
       */
      public static int toJavaInt(LuaValue pos) {
          return pos.optint(1) - 1;
      }
  
      /**
       * convert a lua Int to java Int
       *
       * @param luaInt
       * @return
       */
      public static Integer toJavaInt(Integer luaInt) {
          return luaInt != null ? luaInt - 1 : null;
      }
  
  
      /**
       * convert params to LuaTable
       *
       * @param params
       * @return
       */
      public static LuaValue toTable(List<?> params) {
          if (params != null) {
              final LuaTable result = new LuaTable();
              if (params.size() > 0) {
                  Object value = null;
                  for (int i = 0; i < params.size(); i++) {
                      value = params.get(i);
                      result.set(i + 1, toLuaValue(value));
                  }
              }
              return result;
          }
          return LuaValue.NIL;
      }
  
      /**
       * convert map to LuaTable
       *
       * @param params
       * @return
       */
      public static LuaValue toTable(Map<?, ?> params) {
          if (params != null) {
              final LuaTable result = new LuaTable();
              if (params.size() > 0) {
                  Object valueObj = null;
                  LuaValue key = null;
                  for (final Object keyObj : params.keySet()) {
                      valueObj = params.get(keyObj);
                      key = toLuaValue(keyObj);
                      if (key != LuaValue.NIL) {
                          result.set(key, toLuaValue(valueObj));
                      }
                  }
              }
              return result;
          }
          return LuaValue.NIL;
      }
  
      /**
       * convert luavalue to charsequence
       *
       * @param luaValue
       * @return
       */
      public static CharSequence toText(LuaValue luaValue) {
          if (LuaUtil.isString(luaValue)) {
              return luaValue.optjstring(null);
          } else if (LuaUtil.isUserdata(luaValue)) {
              if (luaValue instanceof UDSpannableString) {
                  return ((UDSpannableString) luaValue).getSpannableStringBuilder();
              } else if (luaValue instanceof UDUnicode) {
                  return ((UDUnicode) luaValue).toString();
              }
          }
          return null;
      }
  
      /**
       * convert object to LuaValue
       *
       * @param value
       * @return
       */
      public static LuaValue toLuaValue(Object value) {
          try {
              if (value instanceof Integer) {
                  return LuaValue.valueOf((Integer) value);
              } else if (value instanceof Long) {
                  return LuaValue.valueOf((Long) value);
              } else if (value instanceof Double) {
                  return LuaValue.valueOf((Double) value);
              } else if (value instanceof String) {
                  return LuaValue.valueOf((String) value);
              } else if (value instanceof Boolean) {
                  return LuaValue.valueOf((Boolean) value);
              } else if (value instanceof byte[]) {
                  return LuaValue.valueOf((byte[]) value);
              } else if (value instanceof List) {
                  return toTable((List) value);
              } else if (value instanceof Map) {
                  return toTable((Map) value);
              } else {
                  return CoerceJavaToLua.coerce(value);
              }
          } catch (Exception e) {
              e.printStackTrace();
              return LuaValue.NIL;
          }
      }
  
      /**
       * convert a table to map
       *
       * @param table
       * @return
       */
      public static Map<String, String> toMap(LuaTable table) {
          if (table != null) {
              final Map<String, String> result = new HashMap<String, String>();
              final LuaValue[] keys = table.keys();
              LuaValue value = null;
              for (LuaValue key : keys) {
                  value = table.get(key);
                  result.put(key.optjstring(null), value.optjstring(null));
              }
          }
          return null;
      }
  }
  