content: /*******************************************************************************
   * Copyright (c) 2012 Luaj.org. All rights reserved.
   * <p/>
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   * <p/>
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   * <p/>
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   ******************************************************************************/
  package org.luaj.vm2;
  
  //TODO 与3.0不一样
  
  import android.content.Context;
  import android.view.ViewGroup;
  
  import com.taobao.luaview.cache.AppCache;
  import com.taobao.luaview.cache.LuaCache;
  import com.taobao.luaview.debug.DebugConnection;
  import com.taobao.luaview.global.LuaResourceFinder;
  import com.taobao.luaview.global.LuaViewConfig;
  import com.taobao.luaview.scriptbundle.LuaScriptManager;
  import com.taobao.luaview.util.LogUtil;
  import com.taobao.luaview.util.LuaUtil;
  
  import org.luaj.vm2.lib.BaseLib;
  import org.luaj.vm2.lib.DebugLib;
  import org.luaj.vm2.lib.PackageLib;
  import org.luaj.vm2.lib.ResourceFinder;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.Reader;
  import java.lang.ref.WeakReference;
  import java.util.Stack;
  
  /**
   * Global environment used by luaj.  Contains global variables referenced by executing lua.
   * <p/>
   * <p/>
   * <h3>Constructing and Initializing Instances</h3>
   * Typically, this is constructed indirectly by a call to
   * {@link JsePlatform.standardGlobasl()} or {@link JmePlatform.standardGlobals()},
   * and then used to load lua scripts for execution as in the following example.
   * <pre> {@code
   * Globals globals = JsePlatform.standardGlobals();
   * globals.load( new StringReader("print 'hello'"), "main.lua" ).call();
   * } </pre>
   * The creates a complete global environment with the standard libraries loaded.
   * <p/>
   * For specialized circumstances, the Globals may be constructed directly and loaded
   * with only those libraries that are needed, for example.
   * <pre> {@code
   * Globals globals = new Globals();
   * globals.load( new BaseLib() );
   * } </pre>
   * <p/>
   * <h3>Loading and Executing Lua Code</h3>
   * Globals contains convenience functions to load and execute lua source code given a Reader.
   * A simple example is:
   * <pre> {@code
   * globals.load(new StringReader("print 'hello'"), "main.lua" ).call();
   * } </pre>
   * <p/>
   * <h3>Fine-Grained Control of Compiling and Loading Lua</h3>
   * Executable LuaFunctions are created from lua code in several steps
   * <ul>
   * <li>find the resource using the platform's {@link ResourceFinder}
   * <li>compile lua to lua bytecode using {@link Compiler}
   * <li>load lua bytecode to a {@link LuaPrototpye} using {@link Undumper}
   * <li>construct {@link LuaClosure} from {@link Prototype} with {@link Globals} using {@link Loader}
   * </ul>
   * <p/>
   * There are alternate flows when the direct lua-to-Java bytecode compiling {@link LuaJC} is used.
   * <ul>
   * <li>compile lua to lua bytecode using {@link Compiler} or load precompiled code using {@link Undumper}
   * <li>convert lua bytecode to equivalent Java bytecode using {@link LuaJC} that implements {@link Loader} directly
   * </ul>
   * <p/>
   * <h3>Java Field</h3>
   * Certain public fields are provided that contain the current values of important global state:
   * <ul>
   * <li>{@link STDIN} Current value for standard input in the laaded IoLib, if any.
   * <li>{@link STDOUT} Current value for standard output in the loaded IoLib, if any.
   * <li>{@link STDERR} Current value for standard error in the loaded IoLib, if any.
   * <li>{@link finder} Current loaded {@link ResourceFinder}, if any.
   * <li>{@link compiler} Current loaded {@link Compiler}, if any.
   * <li>{@link undumper} Current loaded {@link Undumper}, if any.
   * <li>{@link loader} Current loaded {@link Loader}, if any.
   * </ul>
   * <p/>
   * <h3>Lua Environment Variables</h3>
   * When using {@link JsePlatform} or {@link JmePlatform},
   * these environment variables are created within the Globals.
   * <ul>
   * <li>"_G" Pointer to this Globals.
   * <li>"_VERSION" String containing the version of luaj.
   * </ul>
   * <p/>
   * <h3>Use in Multithreaded Environments</h3>
   * In a multi-threaded server environment, each server thread should create one Globals instance,
   * which will be logically distinct and not interfere with each other, but share certain
   * static immutable resources such as class data and string data.
   * <p/>
   *
   * @see org.luaj.vm2.lib.jse.JsePlatform
   * @see org.luaj.vm2.lib.jme.JmePlatform
   * @see LuaValue
   * @see Compiler
   * @see Loader
   * @see Undumper
   * @see ResourceFinder
   * @see LuaC
   * @see LuaJC
   */
  public class Globals extends LuaTable {
  
      public boolean isInited = false;
  
      public boolean isRefreshContainerEnable = true;
      /**
       * Android parent view
       */
      private WeakReference<ViewGroup> mRenderTarget;
      public ViewGroup container;
      private ViewGroup tmpContainer;
  
      //containers
      private Stack<ViewGroup> mContainers;
  
      //luacaches
      private LuaCache mLuaCache = new LuaCache();
  
      /**
       * The current default input stream.
       */
      public InputStream STDIN = null;
  
      /**
       * The current default output stream.
       */
  //    public PrintStream STDOUT = System.out;
  
      /**
       * The current default error stream.
       */
  //    public PrintStream STDERR = System.err;
  
      /**
       * The installed ResourceFinder for looking files by name.
       */
      private ResourceFinder finder;
  
      public LuaResourceFinder getLuaResourceFinder() {
          if (!(finder instanceof LuaResourceFinder)) {
              finder = new LuaResourceFinder(getContext());
          }
          return (LuaResourceFinder) finder;
      }
  
      /**
       * The currently running thread.  Should not be changed by non-library code.
       */
      public LuaThread running = new LuaThread(this);
  
      /**
       * The BaseLib instance loaded into this Globals
       */
      public BaseLib baselib;
  
      /**
       * The PackageLib instance loaded into this Globals
       */
      public PackageLib package_;
  
      /**
       * The DebugLib instance loaded into this Globals, or null if debugging is not enabled
       */
      public DebugLib debuglib;
      public DebugConnection debugConnection;//用作debug
  
      private Boolean isStandardSyntax = null;
  
      /**
       * Interface for module that converts a Prototype into a LuaFunction with an environment.
       */
      public interface Loader {
          /**
           * Convert the prototype into a LuaFunction with the supplied environment.
           */
          LuaFunction load(Prototype prototype, String chunkname, LuaValue env) throws IOException;
      }
  
      /**
       * Interface for module that converts lua source text into a prototype.
       */
      public interface Compiler {
          /**
           * Compile lua source into a Prototype. The InputStream is assumed to be in UTF-8.
           */
          Prototype compile(InputStream stream, String chunkname, boolean standardSyntax) throws IOException;
      }
  
      /**
       * Interface for module that loads lua binary chunk into a prototype.
       */
      public interface Undumper {
          /**
           * Load the supplied input stream into a prototype.
           */
          Prototype undump(InputStream stream, String chunkname) throws IOException;
      }
  
      /**
       * Check that this object is a Globals object, and return it, otherwise throw an error.
       */
      public Globals checkglobals() {
          return this;
      }
  
      /**
       * The installed loader.
       *
       * @see Loader
       */
      public Loader loader;
  
      /**
       * The installed compiler.
       *
       * @see Compiler
       */
      public Compiler compiler;
  
      /**
       * The installed undumper.
       *
       * @see Undumper
       */
      public Undumper undumper;
  
      /**
       * Convenience function for loading a file that is either binary lua or lua source.
       *
       * @param filename Name of the file to load.
       * @return LuaValue that can be call()'ed or invoke()'ed.
       * @throws LuaError if the file could not be loaded.
       */
      public LuaValue loadfile(String filename) {
          try {
              LuaResourceFinder luaFinder = getLuaResourceFinder();
              StringBuffer sb = new StringBuffer();
              if (luaFinder != null) {
                  sb.append(luaFinder.getBaseBundlePath()).append(filename);
              } else {
                  sb.append(this.hashCode()).append("@").append(filename);
              }
              return load(luaFinder.findResource(filename), sb.toString(), "bt", this);
          } catch (Exception e) {
              return error("load " + filename + ": " + e);
          }
      }
  
      /**
       * Convenience function to load a string value as a script.  Must be lua source.
       *
       * @param script    Contents of a lua script, such as "print 'hello, world.'"
       * @param chunkname Name that will be used within the chunk as the source.
       * @return LuaValue that may be executed via .call(), .invoke(), or .method() calls.
       * @throws LuaError if the script could not be compiled.
       */
      public LuaValue load(String script, String chunkname) {
          return load(new StrReader(script), chunkname);
      }
  
      /**
       * Convenience function to load a string value as a script.  Must be lua source.
       *
       * @param script Contents of a lua script, such as "print 'hello, world.'"
       * @return LuaValue that may be executed via .call(), .invoke(), or .method() calls.
       * @throws LuaError if the script could not be compiled.
       */
      public LuaValue load(String script) {
          return load(new StrReader(script), script);
      }
  
      /**
       * Load the content form a reader as a text file.  Must be lua source.
       * The source is converted to UTF-8, so any characters appearing in quoted literals
       * above the range 128 will be converted into multiple bytes.
       */
      public LuaValue load(Reader reader, String chunkname) {
          return load(new UTF8Stream(reader), chunkname, "t", this);
      }
  
      /**
       * Load the content form an input stream as a binary chunk or text file.
       */
      public LuaValue load(InputStream is, String chunkname, String mode, LuaValue env) {
          try {
              if (debugConnection != null) {
                  is.mark(is.available() + 1);
  
                  byte[] bytes = new byte[is.available()];
                  is.read(bytes);
                  is.reset();
  
                  debugConnection.sendScript(bytes, chunkname);
              }
              Prototype p = null;
              if (LuaViewConfig.isCachePrototype() && chunkname != null) {//给prototype解析加cache
                  p = AppCache.getPrototpyeCache().getLru(chunkname);
                  if (p == null) {
                      int psize = is.available();//prototype size
                      p = loadPrototype(is, chunkname, mode);
                      AppCache.getPrototpyeCache().putLru(chunkname, p, psize);
                  }
              } else {
                  p = loadPrototype(is, chunkname, mode);
              }
              return loader.load(p, chunkname, env);
          } catch (LuaError l) {
              throw l;
          } catch (Exception e) {
              return error("load " + chunkname + ": " + e);
          }
      }
  
      /**
       * 直接加载一个prototype
       *
       * @param prototype
       * @param chunkname
       * @return
       */
      public LuaValue load(Prototype prototype, String chunkname) {
          return load(prototype, chunkname, this);
      }
  
      public LuaValue load(Prototype prototype, String chunkname, LuaValue env) {
          try {
              return loader.load(prototype, chunkname, env);
          } catch (Exception e) {
              LogUtil.d("[load prototype error]", chunkname, e);
              return error("load " + chunkname + ": " + e);
          }
      }
  
      /**
       * Load lua source or lua binary from an input stream into a Prototype.
       * The InputStream is either a binary lua chunk starting with the lua binary chunk signature,
       * or a text input file.  If it is a text input file, it is interpreted as a UTF-8 byte sequence.
       */
      public Prototype loadPrototype(InputStream is, String chunkname, String mode) throws IOException {
          if (mode.indexOf('b') >= 0) {
              if (undumper == null)
                  error("No undumper.");
              if (!is.markSupported())
                  is = new BufferedStream(is);
              is.mark(4);
              final Prototype p = undumper.undump(is, chunkname);
              if (p != null)
                  return p;
              is.reset();
          }
          if (mode.indexOf('t') >= 0) {
              return compilePrototype(is, chunkname);
          }
          error("Failed to load prototype " + chunkname + " using mode '" + mode + "'");
          return null;
      }
  
      /**
       * Compile lua source from a Reader into a Prototype. The characters in the reader
       * are converted to bytes using the UTF-8 encoding, so a string literal containing
       * characters with codepoints 128 or above will be converted into multiple bytes.
       */
      public Prototype compilePrototype(Reader reader, String chunkname) throws IOException {
          return compilePrototype(new UTF8Stream(reader), chunkname);
      }
  
      /**
       * Compile lua source from an InputStream into a Prototype.
       * The input is assumed to be UTf-8, but since bytes in the range 128-255 are passed along as
       * literal bytes, any ASCII-compatible encoding such as ISO 8859-1 may also be used.
       */
      public Prototype compilePrototype(InputStream stream, String chunkname) throws IOException {
          if (compiler == null)
              error("No compiler.");
          return compiler.compile(stream, chunkname, isStandardSyntax());
      }
  
      /**
       * Function which yields the current thread.
       *
       * @param args Arguments to supply as return values in the resume function of the resuming thread.
       * @return Values supplied as arguments to the resume() call that reactivates this thread.
       */
      public Varargs yield(Varargs args) {
          if (running == null || running.isMainThread())
              throw new LuaError("cannot yield main thread");
          final LuaThread.State s = running.state;
          return s.lua_yield(args);
      }
  
      /**
       * Reader implementation to read chars from a String in JME or JSE.
       */
      static class StrReader extends Reader {
          final String s;
          int i = 0;
          final int n;
  
          StrReader(String s) {
              this.s = s;
              n = s.length();
          }
  
          public void close() throws IOException {
              i = n;
          }
  
          public int read() throws IOException {
              return i < n ? s.charAt(i++) : -1;
          }
  
          public int read(char[] cbuf, int off, int len) throws IOException {
              int j = 0;
              for (; j < len && i < n; ++j, ++i)
                  cbuf[off + j] = s.charAt(i);
              return j > 0 || len == 0 ? j : -1;
          }
      }
  
      /* Abstract base class to provide basic buffered input storage and delivery.
       * This class may be moved to its own package in the future.
       */
      abstract static class AbstractBufferedStream extends InputStream {
          protected byte[] b;
          protected int i = 0, j = 0;
  
          protected AbstractBufferedStream(int buflen) {
              this.b = new byte[buflen];
          }
  
          abstract protected int avail() throws IOException;
  
          public int read() throws IOException {
              int a = avail();
              return (a <= 0 ? -1 : 0xff & b[i++]);
          }
  
          public int read(byte[] b) throws IOException {
              return read(b, 0, b.length);
          }
  
          public int read(byte[] b, int i0, int n) throws IOException {
              int a = avail();
              if (a <= 0) return -1;
              final int n_read = Math.min(a, n);
              System.arraycopy(this.b, i, b, i0, n_read);
              i += n_read;
              return n_read;
          }
  
          public long skip(long n) throws IOException {
              final long k = Math.min(n, j - i);
              i += k;
              return k;
          }
  
          public int available() throws IOException {
              return j - i;
          }
      }
  
      /**
       * Simple converter from Reader to InputStream using UTF8 encoding that will work
       * on both JME and JSE.
       * This class may be moved to its own package in the future.
       */
      static class UTF8Stream extends AbstractBufferedStream {
          private final char[] c = new char[32];
          private final Reader r;
  
          UTF8Stream(Reader r) {
              super(96);
              this.r = r;
          }
  
          protected int avail() throws IOException {
              if (i < j) return j - i;
              int n = r.read(c);
              if (n < 0)
                  return -1;
              if (n == 0) {
                  int u = r.read();
                  if (u < 0)
                      return -1;
                  c[0] = (char) u;
                  n = 1;
              }
              j = LuaString.encodeToUtf8(c, n, b, i = 0);
              return j;
          }
  
          public void close() throws IOException {
              r.close();
          }
      }
  
      /**
       * Simple buffered InputStream that supports mark.
       * Used to examine an InputStream for a 4-byte binary lua signature,
       * and fall back to text input when the signature is not found,
       * as well as speed up normal compilation and reading of lua scripts.
       * This class may be moved to its own package in the future.
       */
      static class BufferedStream extends AbstractBufferedStream {
          private final InputStream s;
  
          public BufferedStream(InputStream s) {
              this(128, s);
          }
  
          BufferedStream(int buflen, InputStream s) {
              super(buflen);
              this.s = s;
          }
  
          protected int avail() throws IOException {
              if (i < j) return j - i;
              if (j >= b.length) i = j = 0;
              // leave previous bytes in place to implement mark()/reset().
              int n = s.read(b, j, b.length - j);
              if (n < 0)
                  return -1;
              if (n == 0) {
                  int u = s.read();
                  if (u < 0)
                      return -1;
                  b[j] = (byte) u;
                  n = 1;
              }
              j += n;
              return n;
          }
  
          public void close() throws IOException {
              s.close();
          }
  
          public synchronized void mark(int n) {
              if (i > 0 || n > b.length) {
                  byte[] dest = n > b.length ? new byte[n] : b;
                  System.arraycopy(b, i, dest, 0, j - i);
                  j -= i;
                  i = 0;
                  b = dest;
              }
          }
  
          public boolean markSupported() {
              return true;
          }
  
          public synchronized void reset() throws IOException {
              i = 0;
          }
      }
  
      //----------------------------------------------------------------------------------------------
  
      /**
       * call Lua Function
       *
       * @param funName
       * @param objs
       * @return
       */
      public Object callLuaFunction(String funName, Object... objs) {
          if (funName != null) {
              final LuaValue callback = this.get(funName);
              return LuaUtil.callFunction(callback, objs);
          }
          return LuaValue.NIL;
      }
  
      //----------------------------------------------------------------------------------------------
  
      /**
       * 设置是否标准语法，如果为null的时候则依据url来判断
       *
       * @param standardSyntax
       */
      public void setUseStandardSyntax(boolean standardSyntax) {
          isStandardSyntax = standardSyntax;
      }
  
      /**
       * 是否是标准语法
       *
       * @return
       */
      public boolean isStandardSyntax() {
          if (isStandardSyntax != null) {
              return isStandardSyntax;
          } else {
              final LuaResourceFinder finder = getLuaResourceFinder();
              final String url = finder != null ? finder.getUri() : null;
              return !LuaScriptManager.isLuaUnStandardSyntaxUrl(url);
          }
      }
  
      /**
       * 延迟加载库
       *
       * @param binder
       */
      public void tryLazyLoad(final LuaValue binder) {
          load(binder);//load directly
      }
  
      /**
       * 保存原有的container
       *
       * @param viewGroup
       */
      @Deprecated
      public void saveContainer(final ViewGroup viewGroup) {
          if (this.container == null) {
              this.tmpContainer = viewGroup;
              this.container = viewGroup;
          } else {
              this.tmpContainer = this.container;
              this.container = viewGroup;
          }
      }
  
      /**
       * 使用保存的container恢复
       */
      @Deprecated
      public void restoreContainer() {
          this.container = this.tmpContainer;
      }
  
      /**
       * push a container
       *
       * @param viewGroup
       */
      public void pushContainer(final ViewGroup viewGroup) {
          if (this.mContainers == null) {
              this.mContainers = new Stack<ViewGroup>();
          }
          this.mContainers.push(this.container);
          this.container = viewGroup;
      }
  
      /**
       * pop a container
       */
      public void popContainer() {
          if (this.mContainers != null) {
              this.container = this.mContainers.pop();
          }
      }
  
      /**
       * 设置LuaView，对LuaView弱引用
       *
       * @param renderTarget
       */
      public void setRenderTarget(ViewGroup renderTarget) {
          this.mRenderTarget = new WeakReference<ViewGroup>(renderTarget);
      }
  
      /**
       * 获取LuaView
       *
       * @return
       */
      public ViewGroup getRenderTarget() {
          return this.mRenderTarget != null ? this.mRenderTarget.get() : null;
      }
  
  
      public LuaCache getLuaCache() {
          return mLuaCache;
      }
  
      /**
       * 获取context
       *
       * @return
       */
      public Context getContext() {
          ViewGroup renderTarget = this.mRenderTarget != null ? this.mRenderTarget.get() : null;
          return renderTarget != null ? renderTarget.getContext() : null;
      }
  
      /**
       * 获取App级别的Context
       *
       * @return
       */
      public Context getAppContext() {
          return getContext() != null ? getContext().getApplicationContext() : null;
      }
  
      /**
       * 清除container缓存
       */
      public void onDestroy() {
          finder = null;
          mRenderTarget = null;
          container = null;
          tmpContainer = null;
          clearCache();
          mLuaCache = null;
      }
  
      /**
       * clear Cache
       */
      public void clearCache() {
          if (mLuaCache != null) {//清空cache数据
              mLuaCache.clearCachedObjects();//从window中移除的时候清理数据(临时的数据)
          }
          LuaCache.clear();
      }
  }
  