package org.bdware.sc.engine;

import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.analysis.BasicBlock;
import org.bdware.analysis.CFGraph;
import org.bdware.analysis.gas.Evaluates;
import org.bdware.analysis.gas.PPCount;
import org.bdware.sc.ContractProcess;
import org.bdware.sc.ContractResult;
import org.bdware.sc.ContractResult.Status;
import org.bdware.sc.JSEngine;
import org.bdware.sc.bean.Contract;
import org.bdware.sc.bean.ContractRequest;
import org.bdware.sc.bean.ProjectConfig;
import org.bdware.sc.boundry.JavaScriptEntry;
import org.bdware.sc.boundry.Resources;
import org.bdware.sc.boundry.ScriptReturnException;
import org.bdware.sc.boundry.utils.UtilRegistry;
import org.bdware.sc.encrypt.HardwareInfo;
import org.bdware.sc.encrypt.HardwareInfo.OSType;
import org.bdware.sc.event.Event;
import org.bdware.sc.event.REvent;
import org.bdware.sc.event.REvent.REventSemantics;
import org.bdware.sc.node.*;
import org.bdware.sc.syncMech.SyncType;
import org.bdware.sc.trace.ProgramPointCounter;
import org.bdware.sc.util.HashUtil;
import org.bdware.sc.util.JsonUtil;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.MethodNode;
import wrp.jdk.nashorn.api.scripting.NashornException;
import wrp.jdk.nashorn.api.scripting.NashornScriptEngine;
import wrp.jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import wrp.jdk.nashorn.api.scripting.ScriptObjectMirror;
import wrp.jdk.nashorn.internal.objects.Global;
import wrp.jdk.nashorn.internal.runtime.*;

import javax.script.*;
import java.io.*;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class DesktopEngine extends JSEngine {
    private static final String ScriptFileName = "contract_main.yjs";
    private static final Logger LOGGER = LogManager.getLogger(DesktopEngine.class);
    public static boolean _with_init_script = true;
    //    static String script = "";
    public NashornScriptEngine engine;
    public SyncMechUtil syncUtil;
    public boolean recovering; // 如果正在通过trace、trans恢复，设置为true，此时即使是StableMode也不记录
    Resources resources;
    //    Class<?> clz;
    //    byte[] stub;
    YJSClassLoader classLoader;
    private ContractNode cn;
    private Global global;
    //    private Object obj;
    //    private SimpleScriptContext simpleContext;
    // private String traceDir;
    private ContractProcess.Logger tracePS = null;
    private Contract contract;
    private ContractManifest manifest;

    public DesktopEngine() {
        startEngine();
    }

    public DesktopEngine(ContractManifest manifest, String zipPath, Contract contract) {
        File zipFile = new File(zipPath);
        String dirName = zipFile.getName().replaceAll(".zip$", "").replaceAll(".ypk$", "");
        File traceDirFile = new File(zipFile.getParent(), dirName);

        this.contract = contract;
        if (!traceDirFile.exists()) {
            // traceDirFile.mkdirs();
        }
        // traceDir = traceDirFile.getAbsolutePath();
        startEngine();
    }

    public static Object applyWithGlobal(ScriptFunction script, Global global, Object... obj) {
        Global oldGlobal = Context.getGlobal();
        boolean globalChanged = (oldGlobal != global);
        try {
            if (globalChanged) {
                Context.setGlobal(global);
            }
            // System.out.println("[DesktopEngine]" + script.getName() + " -->\n" +
            // script.safeToString());
            Object ret = ScriptRuntime.apply(script, global, obj);
            return ret;
        } catch (NashornException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (globalChanged) {
                Context.setGlobal(oldGlobal);
            }
        }
        return null;
    }

    public void setRecovering(boolean b) {
        recovering = b;
    }

    public NashornScriptEngine getNashornEngine() {
        return engine;
    }

    public Global getDesktopGlobal() {
        return global;
    }

    public YJSClassLoader getClassLoad() {
        return classLoader;
    }

    public void redirectTracePS(ContractProcess.Logger ps) {
        TraceType.ps = tracePS = (ps);
        if (ps instanceof TraceMethod) {
            TraceType.mTracer = (TraceMethod) ps;
        }
    }

    private void startEngine() {
        try {
            syncUtil = new SyncMechUtil(this);
            ClassLoader ccl = Thread.currentThread().getContextClassLoader();
            ccl = (ccl == null) ? NashornScriptEngineFactory.class.getClassLoader() : ccl;
            String[] args = new String[]{"--loader-per-compile=false", "-strict=false"};
            classLoader = new YJSClassLoader(ccl, new YJSFilter());
            engine =
                    (NashornScriptEngine)
                            new NashornScriptEngineFactory()
                                    .getScriptEngine(
                                            args, // "--print-ast",
                                            // "true",
                                            // "-d=/Users/huaqiancai/Downloads/dumpedClz",
                                            // "--trace-callsites=enterexit"
                                            // "--log=methodhandles:all",
                                            // fields:all,
                                            // "--print-parse", "true" "--print-code",
                                            // fields:finest
                                            classLoader);

            Context.TRACEIF = false;
            // engine = (NashornScriptEngine) new
            // NashornScriptEngineFactory().getScriptEngine(new YJSFilter());
            if (_with_init_script) {
                InputStream in =
                        DesktopEngine.class
                                .getClassLoader()
                                .getResourceAsStream("org/bdware/sc/engine/yancloud_desktop.js");
                assert in != null;
                InputStreamReader streamReader = new InputStreamReader(in);
                engine.getContext()
                        .setAttribute(
                                ScriptEngine.FILENAME,
                                "org/bdware/sc/engine/yancloud_desktop.js",
                                ScriptContext.ENGINE_SCOPE);
                engine.eval(streamReader);
            }
            global = engine.getNashornGlobal();
            JavaScriptEntry.currentEngine = engine;
            JavaScriptEntry.currentSyncUtil = syncUtil;
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }

    public void initStubClasses() {
        UtilRegistry.defineUtilClass(classLoader);
    }

    public void setPermission(List<Permission> setPermission) {
        initStubClasses();
        try {
            StringBuilder yancloud_desktop = new StringBuilder();
            List<String> permissionStub = Permission.allName();
            for (Permission permission : setPermission) {
                yancloud_desktop.append(UtilRegistry.getInitStr(permission.name(), true));
                permissionStub.remove(permission.name());
            }
            for (String str : permissionStub) {
                yancloud_desktop.append(UtilRegistry.getInitStr(str, false));
            }
            //            LOGGER.debug("[initScript] " + yancloud_desktop);
            engine.getContext()
                    .setAttribute(
                            ScriptEngine.FILENAME,
                            yancloud_desktop.toString(),
                            ScriptContext.ENGINE_SCOPE);
            engine.eval(yancloud_desktop.toString());
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }

    // byte[]中是字节码
    public Map<String, byte[]> dumpClass() {
        ScriptLoader loader = engine.getNashornContext().getScriptLoader();
        Map<String, byte[]> clzCache = loader.getClzCache();
        Map<String, byte[]> ret = new HashMap<>(clzCache);
        //        for (String str : clzCache.keySet()) {
        //            System.out.println("===ScriptClzName:" + str);
        //        }
        StructureLoader sLoader = (StructureLoader) (engine.getNashornContext().getStructLoader());
        clzCache = sLoader.getClzCache();
        //        for (String str : clzCache.keySet()) {
        //            System.out.println("===StructureClzName:" + str);
        //        }
        ret.putAll(clzCache);
        return ret;
    }

    public void registerResource(Resources resources) {
        Invocable cal = engine;
        this.resources = resources;
        try {
            cal.invokeFunction("defineProp", "Resources", resources);
        } catch (NoSuchMethodException | ScriptException e) {
            e.printStackTrace();
        }
    }

    public Object invokeFunction(String functionName, Object... args) {
        Invocable cal = engine;

        try {
            return cal.invokeFunction(functionName, args);
        } catch (ScriptException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return null;
    }

    public Resources getResources() {
        return this.resources;
    }

    @Override
    public ContractResult loadContract(
            Contract contract, ContractNode contractNode, boolean isInsnLimit) {
        cn = contractNode;
        engine.getContext()
                .setAttribute(ScriptEngine.FILENAME, ScriptFileName, ScriptContext.ENGINE_SCOPE);
        try {
            setPermission(cn.getPermission());
        } catch (Exception e) {
            e.printStackTrace();
        }

        for (FunctionNode fun : contractNode.getFunctions())
            try {
                String str = fun.plainText();
                engine.getContext()
                        .setAttribute(
                                ScriptEngine.FILENAME,
                                fun.getFileName(),
                                ScriptContext.ENGINE_SCOPE);
                compileFunction(fun, str, isInsnLimit);
            } catch (ScriptException e) {
                return wrapperException(e, fun);
            } catch (Exception e) {
                ByteArrayOutputStream bo = new ByteArrayOutputStream();
                e.printStackTrace(new PrintStream(bo));
                e.printStackTrace();
                return new ContractResult(Status.Error, new JsonPrimitive(bo.toString()));
            }
        LOGGER.debug(JsonUtil.toJson(contractNode.events) + "\n\t" + JsonUtil.toJson(contractNode.logs));
        for (String topic : contractNode.events.keySet()) {
            compileEventFunction(topic, topic, contractNode.events.get(topic));
        }
        for (String logName : contractNode.logs.keySet()) {
            String topic = HashUtil.sha3(contract.getID(), logName);
            compileEventFunction(logName, topic, contractNode.logs.get(logName));
        }
        for (ClassNode cn : contractNode.getClzs()) {
            try {
                System.out.println(cn.plainText());
                // engine.eval(cn.plainText());
            } catch (Exception e) {
                // return wrapperException(e, cn.getFileName(), cn.getLine(),
                // cn.getPos());
            }
        }
        ScriptObjectMirror globalVars = (ScriptObjectMirror) engine.get("Global");
        ConfidentialContractUtil.generateConfidentialContract(cn, globalVars, global);
        Context.setGlobal(global);

        ContractResult cResult =
                new ContractResult(Status.Success, new JsonPrimitive(contract.getPublicKey()));
        cResult.isInsnLimit = isInsnLimit;
        return cResult;
    }

    private void compileEventFunction(String name, String topic, REventSemantics semantics) {
        try {
            String str;
            if (REventSemantics.AT_LEAST_ONCE.equals(semantics)) {
                str =
                        String.format(
                                "function %s(arg) { YancloudUtil.pubEvent(\"%s\", arg); }",
                                name, topic);
            } else {
                str =
                        String.format(
                                "function %s(arg) { YancloudUtil.pubEventConstraint(\"%s\", arg, \"%s\"); }",
                                name, topic, semantics.name());
            }
            compileFunction(null, str, false);
            LOGGER.debug("compile function " + name + " success!");
            str =
                    String.format(
                            "function %ss(arg0, arg1) { YancloudUtil.pubEventConstraint(\"%s\", arg0, arg1); }",
                            name, topic);
            compileFunction(null, str, false);
            LOGGER.debug("compile function " + name + "s success!");
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }

    //    /**
    //     * Load a contract into contract engine
    //     *
    //     * @param contractNode a contract node generated by YJS compiler
    //     * @return whether contract is loaded successfully
    //     */
    //    @Override
    //    public ContractResult loadContract(Contract contract, ContractNode contractNode) {
    //        cn = contractNode;
    //        engine.getContext()
    //                .setAttribute(ScriptEngine.FILENAME, ScriptFileName,
    // ScriptContext.ENGINE_SCOPE);
    //        try {
    //            setPermission(cn.getPermission());
    //        } catch (Exception e) {
    //            e.printStackTrace();
    //        }
    //
    //        for (FunctionNode fun : cn.getFunctions()) {
    //            try {
    //                String str = fun.plainText();
    //                engine.getContext()
    //                        .setAttribute(
    //                                ScriptEngine.FILENAME,
    //                                fun.getFileName(),
    //                                ScriptContext.ENGINE_SCOPE);
    //                Object scriptFunction = engine.eval(str);
    //                ScriptObjectMirror sf = (ScriptObjectMirror) scriptFunction;
    //                compileFunction(sf, fun.getLogTypes().contains(LogType.Branch));
    //            } catch (ScriptException e) {
    //                return wrapperException(e, fun);
    //            } catch (Exception e) {
    //                ByteArrayOutputStream bo = new ByteArrayOutputStream();
    //                e.printStackTrace(new PrintStream(bo));
    //                e.printStackTrace();
    //                return new ContractResult(Status.Error, new JsonPrimitive(bo.toString()));
    //            }
    //        }
    //        for (String event : cn.events) {
    //            String str =
    //                    "function "
    //                            + event
    //                            + "(arg){ return YancloudUtil.pubEvent(\""
    //                            + event
    //                            + "\",arg);}";
    //
    //            try {
    //                Object scriptFunction = engine.eval(str);
    //                ScriptObjectMirror sf = (ScriptObjectMirror) scriptFunction;
    //                compileFunction(sf, false);
    //            } catch (ScriptException e) {
    //                e.printStackTrace();
    //            }
    //        }
    //
    //        for (ClassNode classNode : cn.getClzs()) {
    //            try {
    //                System.out.println(classNode.plainText());
    //                // engine.eval(cn.plainText());
    //            } catch (Exception e) {
    //                // return wrapperException(e, cn.getFileName(), cn.getLine(),
    //                // cn.getPos());
    //            }
    //        }
    //
    //        // dump confidential functions and corresponding dependent functions to a String in
    // Global
    //        // variable.
    //        // The String will be passed to collect signature.
    //
    //        return new ContractResult(Status.Success, new JsonPrimitive(""));
    //    }
    private void compileFunction(FunctionNode functionNode, ScriptObjectMirror sf, boolean instrumentBranch) {
        Global oldGlobal = Context.getGlobal();
        boolean globalChanged = (oldGlobal != global);
        try {
            if (globalChanged) {
                Context.setGlobal(global);
            }
            if (instrumentBranch) {
                Context.TRACEIF = true;
                Context.TRACEMETHOD = true;
            }
            sf.compileScriptFunction();
            ScriptFunction scriptFunction = (ScriptFunction) sf.getScriptObject();
            Field f = ScriptFunction.class.getDeclaredField("data");
            f.setAccessible(true);
            ScriptFunctionData scriptFunctioNData = (ScriptFunctionData) f.get(scriptFunction);
            Object scope = scriptFunction.getScope();
            Method getGeneric = ScriptFunctionData.class.getDeclaredMethod("getGenericInvoker", ScriptObject.class);
            getGeneric.setAccessible(true);
            MethodHandle methodHandle = (MethodHandle) getGeneric.invoke(scriptFunctioNData, scope);
            if (methodHandle.getClass() != Class.forName("java.lang.invoke.DirectMethodHandle")) {
                Field argL0 = methodHandle.getClass().getDeclaredField("argL0");
                argL0.setAccessible(true);
                methodHandle = (MethodHandle) argL0.get(methodHandle);

            }
            Field memberNameField = methodHandle.getClass().getDeclaredField("member");

            memberNameField.setAccessible(true);
            Object memberName = memberNameField.get(methodHandle);
            Field clazz = memberName.getClass().getDeclaredField("clazz");
            clazz.setAccessible(true);
            Class clazz2 = (Class) clazz.get(memberName);
            if (functionNode != null)
                functionNode.compiledClazz = clazz2;
            //functionNode==null --> event functions
        } catch (Exception e) {

            e.printStackTrace();
        } finally {
            if (globalChanged) Context.setGlobal(oldGlobal);
            Context.TRACEIF = false;
        }
    }

    public void compileFunction(FunctionNode functionNode, String snippet, boolean instrumentBranch) throws ScriptException {
        compileFunction(functionNode, (ScriptObjectMirror) engine.eval(snippet), instrumentBranch);
    }


    @Override
    public ContractResult executeContract(ContractRequest input) {
        Global oldGlobal = Context.getGlobal();
        boolean globalChanged = (oldGlobal != global);
        if (globalChanged) {
            Context.setGlobal(global);
        }
        ContractProcess.Logger previous = this.getTracePS();
        ByteArrayOutputStream bo = null;
        if (syncUtil.startFlag && syncUtil.currType == SyncType.Trace && !recovering) {
            syncUtil.traceRecordUtil.startNext();
        } else if (syncUtil.startFlag && syncUtil.currType == SyncType.Trans && !recovering) {
            if (input.needSeq) {
                syncUtil.transRecordUtil.startNext(input.getAction(), input.getArg(), input.seq);
            } else {
                syncUtil.transRecordUtil.startNext(input.getAction(), input.getArg());
            }
        }
        // TODO remove this in executeContract, using getFunctionNodes in ContractManger instead.
        if (input.getAction().equals("getFunctionNodes")) {
            return getFunctionNodes();
        }
        JavaScriptEntry.msgList = new ArrayList<>();
        FunctionNode fun = cn.getFunction(input.getAction());
        if (fun == null) {
            return new ContractResult(
                    Status.Exception,
                    new JsonPrimitive("Action " + input.getAction() + " is not exists"));
        }
        ProgramPointCounter ppc = null;
        try {
            if (fun.getCost() != null && fun.getCost().isCountGas()) {
                List<String> functions = new ArrayList<>();
                for (FunctionNode fn : cn.getFunctions()) {
                    if (fn.isExport()) {
                        functions.add(fn.functionName);
                    }
                }
                int functionIndex = functions.indexOf(input.getAction());
                // TODO calculate ppCountMap at loading time?
                HashMap<String, Long> ppCountMap = evaluatesAnalysis(input.getAction(), functions);
                Long extraGas = getExtraGas(fun.getCost().getExtraGas(), input);
                bo = new ByteArrayOutputStream();
                ppc =
                        new ProgramPointCounter(
                                bo,
                                previous.getCp(),
                                Long.MAX_VALUE,
                                functionIndex,
                                input.getGasLimit(),
                                extraGas,
                                input.getAction(),
                                ppCountMap);
                this.redirectTracePS(ppc);
            }
            if (fun.isExport() ||
                    // if the function has been registered as event handler
                    (fun.isHandler() &&
                            null != input.getRequester() &&
                            input.getRequester().startsWith("event"))) {
                Object ret;
                if (fun.isView()) {
                    ret = executeWithoutLock(fun, input, null);
                } else {
                    synchronized (this) {
                        ret = executeWithoutLock(fun, input, null);

                    }
                }
                //System.out.println("[DesktopEngine MaskConfig]"+ContractProcess.instance.getProjectConfig().getMaskConfig().config.toString());
                ContractResult contractRes = new ContractResult(Status.Success, JSONTool.convertMirrorToJson(ret));
                if (ppc != null) {
                    contractRes.extraGas = ppc.extraGas;
                    contractRes.executionGas = ppc.cost;
                    contractRes.totalGas = ppc.extraGas + ppc.cost;
                }
                if (bo != null) {
                    contractRes.analysis = bo.toString();
                }

                if (fun.getLogTypes().contains(LogType.Branch)) {
                    contractRes.branch = tracePS.getOutputStr();
                }

                List<REvent> msgList = JavaScriptEntry.msgList;
                JavaScriptEntry.msgList = null;
                if (msgList != null && !msgList.isEmpty()) {
                    contractRes.events = msgList;
                    contractRes.eventRelated = true;
                }

                if (fun.isHandler() && input.getRequester().length() == 40) {
                    contractRes.eventRelated = true;
                }
                if (syncUtil.startFlag && !recovering) {
                    switch (syncUtil.currType) {
                        case Trace:
                            //  syncUtil.traceRecordUtil.eachFinish();
                            break;
                        case Trans:
                            //    syncUtil.transRecordUtil.eachFinish();
                            break;
                        case Memory:
                        default:
                            break;
                    }
                }

                return contractRes;
            } else {
                // return new ContractResult(Status.Exception, "Action " + input.getAction() + "
                // is not exported!");
                return new ContractResult(
                        Status.Exception,
                        new JsonPrimitive("Action " + input.getAction() + " is not exported!"));
            }

        } catch (ScriptReturnException e) {
            e.printStackTrace();
            return new ContractResult(
                    Status.Exception,
                    e.message);
        } catch (ScriptException e) {
            Throwable cause = e.getCause();
            e.printStackTrace();
            return wrapperException(e, fun);
        } catch (Throwable e) {
            ByteArrayOutputStream bo1 = new ByteArrayOutputStream();
            PrintStream ps = new PrintStream(bo1);
            e.printStackTrace(ps);
            e.printStackTrace();
            if (e.getCause() != null && e.getCause() instanceof ScriptException) {
                return new ContractResult(
                        Status.Exception,
                        new JsonPrimitive(
                                extractException(bo1.toString(), extract(cn, e.getCause()))));
            } else {
                return new ContractResult(
                        Status.Exception,
                        new JsonPrimitive(extractException(bo1.toString(), extract(cn, e))));
            }
        } finally {
            this.redirectTracePS(previous);
            if (globalChanged) Context.setGlobal(oldGlobal);
        }

    }

    public Object executeWithoutLock(FunctionNode fun, ContractRequest input, Object injectedArg) throws ScriptException, NoSuchMethodException {
        //              long start = System.currentTimeMillis();
        ArgPacks argPacks = new ArgPacks(input, input.getArg(), null);
        if(injectedArg != null) {
            argPacks.arg = injectedArg;
        }
        for (AnnotationHook handler : fun.beforeExecutionAnnotations()) {
            argPacks = handler.handle(this, argPacks);
        }

        // actually invoke!
        if (argPacks.ret == null) {
            argPacks.ret =
                    engine.invokeFunction(
                            input.getAction(),
                            (fun.isHandler()
                                    ? JsonUtil.fromJson(input.getArg(), Event.class)
                                    : JSONTool.convertJsonElementToMirror(argPacks.arg)),
                            input.getRequester(),
                            input.getRequesterDOI());
        }

        for (AnnotationHook handler : fun.afterExecutionAnnotations()) {
            //Mask在after裏面
            //System.out.println("afterHook"+contract.Mask);
            argPacks = handler.handle(this, argPacks);
        }
        return argPacks.ret;
    }

    private String extractException(String msg, List<String> stack) {
        int endIndex = Math.min(msg.indexOf("in"), msg.length());
        StringBuilder msb = new StringBuilder(msg.substring(0, endIndex));
        for (String str : stack) {
            msb.append("\n").append(str);
        }
        return msb.toString();
    }

    private HashMap<String, Long> evaluatesAnalysis(String getFunction, List<String> functions) {
        // System.out.println("当前的function:" + getFunction);
        HashMap<String, CFGraph> CFGmap = new HashMap<>();
        HashMap<String, Long> ppCountMap = new HashMap<>();
        Map<String, byte[]> clzs = this.dumpClass();
        Map<String, MethodNode> methods = new HashMap<>();
        for (byte[] clz : clzs.values()) {
            org.objectweb.asm.tree.ClassNode classNode = new org.objectweb.asm.tree.ClassNode();
            ClassReader cr = new ClassReader(clz);
            cr.accept(classNode, ClassReader.EXPAND_FRAMES);
            for (MethodNode mn : classNode.methods) {
                methods.put(mn.name, mn);
            }
        }
        int flag = 0;
        for (String function : functions) {
            MethodNode mn = methods.get(function);
            if (mn != null) {
                CFGraph cfg =
                        new CFGraph(mn) {
                            @Override
                            public BasicBlock getBasicBlock(int id) {
                                return new BasicBlock(id);
                            }
                        };
                // cfg.printSelf();
                CFGmap.put(function, cfg);
                PPCount countFee = new PPCount(cfg, flag);

                BasicBlock bb = cfg.getBasicBlockAt(0);
                countFee.dfs(cfg, bb);
                // System.out.println("[ppmap]:" + PPCount.ppMap);
                // System.out.println("[PPCount.branchCount]"+PPCount.branchCount);
                Evaluates feEvaluates = new Evaluates();
                feEvaluates.getGas(PPCount.branchCount);
                feEvaluates.getInsnGas(PPCount.ppMap);

                PPCount.countFunction(function, Evaluates.map);
                ppCountMap = Evaluates.map;
                // System.out.println("+++++++" + PPCount.ppMap);
                flag++;
            }
        }
        /*        for (Map.Entry<String, Long> map : PPCount.functionSumGas.entrySet()) {
            if (map.getKey().contains(getFunction) && map.getKey().contains("true")) {
                System.out.println("[合约方法pub中条件循环为true时：]" + map.getValue());
            } else if (map.getKey().contains(getFunction) && map.getKey().contains("false")) {
                System.out.println("[合约方法pub中条件循环为false时：]" + map.getValue());
            } else if (map.getKey().contains(getFunction)) {
                System.out.println("[合约方法pub中其他语句消耗：]" + map.getValue());
            }
        }*/
        return ppCountMap;
    }

    private Long getExtraGas(String costFunction, ContractRequest input)
            throws ScriptException, NoSuchMethodException {
        if (costFunction == null || costFunction.equals("")) {
            return 0L;
        }
        Invocable cal = engine;
        Object ret =
                cal.invokeFunction(
                        costFunction,
                        input.getArg(),
                        input.getRequester(),
                        input.getRequesterDOI());
        if (ret != null && StringUtils.isNumeric(ret.toString())) {
            return Long.parseLong(ret.toString());
        } else {
            return 0L;
        }
    }

    private ContractResult getFunctionNodes() {
        String msg = JsonUtil.toJson(cn.getFunctions());
        return new ContractResult(Status.Exception, new JsonPrimitive(msg));
    }

    private void dump(Class<?> clz) {
        try {
            String name = clz.getCanonicalName().replace(".", "/") + ".class";
            System.out.println("DumpClzz:" + clz.getCanonicalName() + " -->" + name);
            name = "/Script.class";

            InputStream input = clz.getClassLoader().getResourceAsStream(name);
            FileOutputStream fout =
                    new FileOutputStream("./output/" + clz.getCanonicalName() + ".class");
            byte[] arr = new byte[1024];
            int l;
            assert input != null;
            while ((l = input.read(arr)) > 0) {
                fout.write(arr, 0, l);
            }
            fout.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private LinkedHashMap<?, ?> getClassCache(Context c) {
        try {
            Field f = c.getClass().getDeclaredField("classCache");
            f.setAccessible(true);
            Object classCache = f.get(c);
            return (LinkedHashMap<?, ?>) classCache;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private ContractResult wrapperException(ScriptException e, FunctionNode fun) {
        int line = fun.getLine();
        int pos = fun.getPos();
        StringBuilder content = new StringBuilder();
        String message = e.getMessage();
        message = message.replaceFirst("^[^:]*:[^:]*:[^ ]* ", "");
        int actLine = e.getLineNumber();
        if (actLine != -1) {
            actLine += line - 1;
        }
        message =
                message.replaceAll(
                        "at line number " + e.getLineNumber(), "at line number " + (actLine));
        if (fun.getFileName() != null)
            message = message.replace("in contract_main.yjs", "in " + fun.getFileName());
        content.append(message);
        content.append("(");
        if (fun.functionName != null) content.append(fun.functionName);
        else content.append("contract_main.yjs");
        content.append(":");

        content.append(actLine);
        content.append(":").append(pos).append(")");
        return new ContractResult(Status.Exception, new JsonPrimitive(content.toString()));
    }

    private List<String> extract(ContractNode c, Throwable cause) {
        StackTraceElement[] stack = cause.getStackTrace();
        List<String> ret = new ArrayList<>();
        for (StackTraceElement element : stack) {

            if (element == null || element.getFileName() == null) {
                continue;
            }
            String methodName = element.getMethodName();
            String fileName = element.getFileName();

            if (fileName.endsWith(".java")) continue;
            if (c.isBundle()) {
                fileName = fixFile(c, methodName);
            }
            if (fileName.equals("--")) continue;
            ret.add(
                    String.format(
                            "at %s(%s:%d)",
                            methodName,
                            fileName,
                            (fixLine(c, methodName) + element.getLineNumber())));
        }
        return ret;
    }

    private String fixFile(ContractNode c, String methodName) {
        return c.queryFile(methodName);
    }

    private int fixLine(ContractNode c, String methodName) {
        return c.queryLine(methodName) - 1;
    }

    public Global getGlobal() {
        return engine.getNashornGlobal();
    }

    @Override
    public Object eval(String script, ScriptContext context) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Object eval(Reader reader, ScriptContext context) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Object eval(String script) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Object eval(Reader reader) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Object eval(String script, Bindings n) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Object eval(Reader reader, Bindings n) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void put(String key, Object value) {
        // TODO Auto-generated method stub

    }

    @Override
    public Object get(String key) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Bindings getBindings(int scope) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setBindings(Bindings bindings, int scope) {
        // TODO Auto-generated method stub

    }

    @Override
    public Bindings createBindings() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public ScriptContext getContext() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setContext(ScriptContext context) {
        // TODO Auto-generated method stub

    }

    @Override
    public ScriptEngineFactory getFactory() {
        // TODO Auto-generated method stub
        return null;
    }

    public void loadJar(ZipFile zf) {
        YJSClassLoader loader = getAppLoader();
        Enumeration<? extends ZipEntry> entries = zf.entries();
        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();
            if (entry.isDirectory()) {
                continue;
            }
            try {
                assert null != loader;
                if (entry.getName().endsWith(".jar")) {
                    loader.loadJar(zf.getInputStream(entry), entry.getName().replaceAll(".*/", ""));
                }
                if (entry.getName().endsWith(".so") || entry.getName().endsWith(".so.1")) {
                    System.out.println("unzip library:" + entry.getName().replaceAll(".*/", ""));
                    loader.unzipLibrary(
                            zf.getInputStream(entry), entry.getName().replaceAll(".*/", ""));
                }
                if (HardwareInfo.type == OSType.mac && entry.getName().endsWith(".dylib")) {
                    System.out.println("unzip library:" + entry.getName().replaceAll(".*/", ""));
                    loader.unzipLibrary(
                            zf.getInputStream(entry), entry.getName().replaceAll(".*/", ""));
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (null != loader) {
            loader.loadLibraries();
        }
    }

    private YJSClassLoader getAppLoader() {
        try {
            Field f = engine.getClass().getDeclaredField("nashornContext");
            f.setAccessible(true);
            Context c = (Context) f.get(engine);
            Method m = Context.class.getDeclaredMethod("getAppLoader");
            m.setAccessible(true);
            return (YJSClassLoader) m.invoke(c);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // TODO Auto-generated method stub
        return null;
    }

    public void setManifest(ContractManifest manifest) {
        this.manifest = manifest;
    }

    public ContractManifest getManifest() {
        return this.manifest;
    }

    public ContractProcess.Logger getTracePS() {
        return tracePS;
    }

    public Contract getContract() {
        return this.contract;
    }

    public ProjectConfig getProjectConfig() {
        return ContractProcess.instance.getProjectConfig();
    }
}
