package org.bdware.sc.handler;

import com.google.gson.reflect.TypeToken;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.sc.ContractProcess;
import org.bdware.sc.bean.Contract;
import org.bdware.sc.conn.Description;
import org.bdware.sc.conn.MsgHandler;
import org.bdware.sc.conn.ResultCallback;
import org.bdware.sc.conn.ServiceServer;
import org.bdware.sc.get.GetMessage;
import org.bdware.sc.util.JsonUtil;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ContractHandler extends MsgHandler implements Runnable {
    private static final Logger LOGGER = LogManager.getLogger(ContractHandler.class);
    ContractProcess cs;
    String identifier = "null";

    public ContractHandler(ContractProcess cs) {
        this.cs = cs;
    }

    public void run() {
        System.out.println("ContractHandler: exit in 2 seconds!");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.exit(0);
    }

    @Description("start sync mech")
    public void startSync(GetMessage msg, ResultCallback cb) {
        cs.startSync();
        cb.onResult("success");
    }

    @Description("stop sync mech")
    public void stopSync(GetMessage msg, ResultCallback cb) {
        cs.stopSync();
        cb.onResult("success");
    }

    @Description("set ContractRecord file name")
    public void setCRFile(GetMessage msg, ResultCallback cb) {
        cs.setCRFile(msg.arg);
        cb.onResult("success");
    }

    @Description("set contractDir")
    public void setDir(GetMessage msg, ResultCallback cb) {
        System.out.println("ContractHandler setDir");
        cs.setDir(msg.arg);
        cb.onResult("success");
    }

    @Description("getDebug")
    public void getDebug(GetMessage msg, ResultCallback cb) {
        LOGGER.debug("getDebug");
        cb.onResult(JsonUtil.toJson(cs.isDebug()));
    }

    @Description("clearSyncFiles")
    public void clearSyncFiles(GetMessage msg, ResultCallback cb) {
        cs.clearSyncFiles(msg.arg);
        cb.onResult("success");
    }

    @Description("changeDumpPeriod")
    public void changeDumpPeriod(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.changeDumpPeriod(msg.arg));
    }

    @Description("getDumpPeriod")
    public void getDumpPeriod(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getDumpPeriod());
    }

    @Description("getCachedTransRecords")
    public void getCachedTransRecords(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getCachedTransRecords(msg.arg));
    }

    @Description("start auto dump")
    public void startAutoDump(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.startAutoDump());
    }

    @Description("register manager port")
    public void registerMangerPort(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.registerMangerPort(msg.arg));
    }

    @Description("set current ContractBundle")
    public void setContractBundle(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.setContractBundle(JsonUtil.fromJson(msg.arg, Contract.class)));
    }

    @Description("set members")
    public void setMembers(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.setMembers(JsonUtil.fromJson(msg.arg, new TypeToken<List<String>>() {
        }.getType())));
    }

    @Description("get current contract name")
    public void getContractName(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getContractName());
    }

    @Description("set current Contract")
    public void setContract(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.setContract(JsonUtil.fromJson(msg.arg, Contract.class)));
    }

    @Description(value = "execute contract", isAsync = true)
    public void executeContract(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.executeContract(msg.arg));
    }

    @Description(value = "invoke function without limit")
    public void invokeFunctionWithoutLimit(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.executeFunctionWithoutLimit(msg.arg));
    }

    @Description("set DesktopPermission")
    public void setDesktopPermission(GetMessage msg, ResultCallback cb) {
        String result = cs.setDesktopPermission(msg.arg);
        cb.onResult(result);
    }

    @Description("change debug Flag")
    public void changeDebugFlag(GetMessage msg, ResultCallback cb) {
        String result = cs.changeDebugFlag(Boolean.valueOf(msg.arg));
        cb.onResult(result);
    }

    @Description("get functionEvaluates")
    public void functionEvaluates(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.evaluatesAnalysis(msg.arg));
    }

    @Description("get memory set")
    public void getMemorySet(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getMemorySet());
    }

    @Description("get logType")
    public void getLogType(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getLogType(msg.arg));
    }

    @Description("exit Contract!")
    public void suicide(GetMessage msg, ResultCallback cb) {
        Map<String, Object> ret = new HashMap<>();
        ret.put("status", "success");
        cs.beforeSuicide();
        if (cs.checkSub()) {
            ret.put("cleanSub", true);
        }
        ServiceServer.executor.execute(this);
        cb.onResult(JsonUtil.toJson(ret));
    }

    @Description("check alive")
    public void ping(GetMessage msg, ResultCallback cb) {
        cb.onResult("pong");
    }

    @Description("dump contract process memory")
    public void getMemoryDump(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getMemoryDump(msg.arg));
    }

    @Description("get memory usage")
    public void getStorage(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getStorage());
    }

    @Description("redo by local trans record")
    public void redo(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.redo(msg.arg));
    }

    @Description("load dumped memory")
    public void loadMemory(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.loadMemoryDump(msg.arg));
    }

    @Description("setDBInfo, the db is used to store local logs")
    public void setDBInfo(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.setDBInfo(msg.arg));
    }

    @Description("getUsedMemory")
    public void getUsedMemory(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getUsedMemory("") + "");
    }

    @Description("showPermission")
    public void showPermission(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.showPermission());
    }

    @Description("is signature required?")
    public void isSigRequired(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.isSigRequired() + "");
    }

    @Description("Get Declared Events")
    public void getDeclaredEvents(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getDeclaredEvents());
    }

    @Description("Get Contract Annotations")
    public void getAnnotations(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getAnnotations());
    }

    @Description("Get Exported Functions")
    public void getExportedFunctions(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getExportedFunctions());
    }

    @Description("Whether current process is contract process, always cmi")
    public void isContractProcess(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.cmi);
    }

    @Description("get contract")
    public void getContract(GetMessage msg, ResultCallback cb) {
        cb.onResult(JsonUtil.toJson(cs.getContract()));
    }

    @Description("setPID")
    public void setPID(GetMessage msg, ResultCallback cb) {
        cs.setPID(msg.arg);
        cb.onResult("success");
    }

    @Description("setProjectConfig")
    public void setProjectConfig(GetMessage msg, ResultCallback cb) {
        LOGGER.debug("ContractHandler: " + msg.arg);
        cs.setProjectConfig(msg.arg);
        cb.onResult("success");
    }

    @Description("getPID")
    public void getPID(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getPID());
    }

    @Description("requestLogSize")
    public void getLogSize(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.logSize() + "");
    }

    @Description("request Log")
    public void requestLog(GetMessage msg, ResultCallback cb) {
        String[] data = msg.arg.split(",");
        cb.onResult(cs.requestLog(Long.parseLong(data[0]), Integer.parseInt(data[1])));
    }

    @Description("request LastLog")
    public void requestLastLog(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.requestLast(Integer.parseInt(msg.arg)));
    }

    @Description("setIdentifier")
    public void setIdentifier(GetMessage msg, ResultCallback cb) {
        identifier = msg.arg;
        cb.onResult("success");
    }

    @Description("getIdentifier")
    public void getIdentifier(GetMessage msg, ResultCallback cb) {
        cb.onResult(identifier);
    }

    @Description("getControlFlow")
    public void getControlFlow(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getControlFlow(JsonUtil.fromJson(msg.arg, Contract.class)));
    }

    @Description("getStateful")
    public void getStateful(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getStateful());
    }

    @Description("parseYpkPermissions")
    public void parseYpkPermissions(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.parseYpkPermissions(msg.arg));
    }

    @Description("staticVerify")
    public void staticVerify(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.staticVerify(JsonUtil.fromJson(msg.arg, Contract.class)));
    }

    @Description("getDependentContracts")
    public void getDependentContracts(GetMessage msg, ResultCallback cb) {
        cb.onResult(cs.getDependentContracts());
    }


}
