package org.bdware.sc.py;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.bdware.sc.py.bean.*;
import org.bdware.sc.py.utils.HttpClient;
import org.bdware.sc.py.utils.HttpExt;
import org.bdware.sc.util.JsonUtil;

import java.util.ArrayList;
import java.util.List;

public class PYEntry {

    public static PYEntry instance = new PYEntry();
    final String ip = "127.0.0.1";
    final Integer port = 5000;

    public static String getModuleMethodSig(
            String packageName, String moduleName, String methodName) {
        return "json";
    }

    /**
     * 目前同名不同参，需要添加multimethod装饰器，暂时不考虑那么复杂
     */
    public static String getModuleClzMethodSig(
            String packageName, String moduleName, String className, String methodName) {
        return "json";
    }

    public static String getModuleClzClassMethodSig(
            String packName, String moduleName, String className, String methodName) {
        return "json";
    }

    public static String getModuleClzStaticMethodSig(
            String packName, String moduleName, String className, String methodName) {
        return "json";
    }

    public String creatObject(String classFullName, PYMethodParams PYMethodParams) {
        System.out.println("[PYEntry] createObject");
        String urlPath = "/api/createObj/";
        String response = null;

        PYYjsParams pyYjsParams = new PYYjsParams();
        pyYjsParams.setPackage_name(HttpExt.getPackageName(HttpExt.getModuleName(classFullName)));
        pyYjsParams.setModule_name(HttpExt.getModuleName(classFullName));
        pyYjsParams.setModule_class_name(HttpExt.getClassName(classFullName));
        pyYjsParams.setParams(PYMethodParams);

        PYYjsRequest pyYjsRequest = new PYYjsRequest();
        pyYjsRequest.setYjsParams(pyYjsParams);

        response = HttpClient.post(ip, port, urlPath, JsonUtil.toJson(pyYjsRequest));
        JsonObject pyResponse = JsonUtil.parseStringAsJsonObject(response);
        return pyResponse.getAsJsonObject("yjsResult").get("objectId").getAsString();
    }

    public String createBuiltinsObj(String moduleClassName, PYMethodParams PYMethodParams) {
        String urlPath = "/api/createBuiltinsObj/";
        String httpResponse;

        PYYjsRequest pyYjsRequest = new PYYjsRequest();
        PYYjsParams pyYjsParams = new PYYjsParams();
        if (moduleClassName.length() != 0) {
            pyYjsParams.setModule_class_name(moduleClassName);
        }
        pyYjsParams.setParams(PYMethodParams);
        pyYjsRequest.setYjsParams(pyYjsParams);

        httpResponse = HttpClient.post(ip, port, urlPath, JsonUtil.toJson(pyYjsRequest));

        JsonObject pyResponse = JsonUtil.parseStringAsJsonObject(httpResponse);
        return pyResponse.getAsJsonObject("yjsResult").get("objectId").getAsString();
    }

    public List<PYPackage> availablePackages() {
        String urlPath = "/api/availablePackages/";
        String response;
        response = HttpClient.get(ip, port, urlPath);
        JsonObject pyResponse = JsonUtil.parseStringAsJsonObject(response);
        JsonObject availablePackages =
                pyResponse.getAsJsonObject("yjsResult").getAsJsonObject("availablePackages");
        JsonArray array = availablePackages.getAsJsonArray("packages");
        List<PYPackage> pyPackages = new ArrayList<>();

        for (int i = 0; i < array.size(); i++) {
            PYPackage pyPackage = new PYPackage();
            pyPackage.setName(array.get(i).getAsString());
            pyPackages.add(pyPackage);
        }
        return pyPackages;
    }

    public List<PYModule> availableModules() {
        String urlPath = "/api/availableModules/";
        String response;
        response = HttpClient.get(ip, port, urlPath);
        JsonObject pyResponse = JsonUtil.parseStringAsJsonObject(response);
        JsonObject availablePackages =
                pyResponse.getAsJsonObject("yjsResult").getAsJsonObject("availableModules");
        JsonArray array = availablePackages.getAsJsonArray("modules");
        // "yjsResult": {"availableModules": {"modules": ["sample", "yjsexample",
        // "yjsexample.sample"]}}
        List<PYModule> pyModules = new ArrayList<>();
        for (int i = 0; i < array.size(); i++) {
            PYModule pyModule = new PYModule();
            pyModule.setName(array.get(i).getAsString());
            pyModules.add(pyModule);
        }
        return pyModules;
    }

    public PYPackage packageInfo(String pkgName) {
        String urlPath = "/api/packageAllInfo/";
        String httpResponse;

        PYYjsParams pyYjsParams = new PYYjsParams();
        pyYjsParams.setPackage_name(pkgName);
        PYYjsRequest pyYjsRequest = new PYYjsRequest();
        pyYjsRequest.setYjsParams(pyYjsParams);
        httpResponse = HttpClient.post(ip, port, urlPath, JsonUtil.toJson(pyYjsRequest));

        // "yjsResult": {"packageTotalInfo": {"package_name": "yjsexample",
        // "module_names": ["yjsexample.sample"]}}
        JsonObject pyResponse = JsonUtil.parseStringAsJsonObject(httpResponse);
        JsonObject packageTotalInfo =
                pyResponse.getAsJsonObject("yjsResult").getAsJsonObject("packageTotalInfo");
        PackageInfo packageInfo = JsonUtil.fromJson(packageTotalInfo, PackageInfo.class);
        PYPackage pyPackage = new PYPackage();
        pyPackage.setName(packageInfo.getPackage_name());

        List<PYModule> moduleList = new ArrayList<>();
        List<String> moduleNames = packageInfo.getModule_names();
        for (String name : moduleNames) {
            PYModule pyModule = new PYModule();
            pyModule.setName(name);
            // todo: add module info
            moduleList.add(pyModule);
        }
        pyPackage.setModules(moduleList);
        return pyPackage;
    }

    public PYModule moduleInfo(String moduleFullName) {
        String urlPath = "/api/moduleInfo/";
        String httpResponse;

        PYYjsRequest pyYjsRequest = new PYYjsRequest();
        PYYjsParams pyYjsParams = new PYYjsParams();
        pyYjsParams.setModule_name(moduleFullName);
        pyYjsRequest.setYjsParams(pyYjsParams);
        httpResponse = HttpClient.post(ip, port, urlPath, JsonUtil.toJson(pyYjsRequest));

        // "yjsResult": {"moduleInfo": {"module_func": [{"func_sig": "(arg_a)",
        // "func_name": "moduleFunc1"}], "classes": [{"class_sig": {"init_sig": "(self,
        // age)"}, "class_method": [{"method_sign": "()", "method_name":
        // "classMethod"}], "class_func": [{"func_sign": "(self, name)", "func_name":
        // "sayHello"}, {"func_sign": "(self, name)", "func_name": "sayHello"}],
        // "class_name": "A"}
        ModuleInfo moduleInfo;
        JsonObject pyResponse = JsonUtil.parseStringAsJsonObject(httpResponse);
        JsonObject moduleInfoObj =
                pyResponse.getAsJsonObject("yjsResult").getAsJsonObject("moduleInfo");
        moduleInfo = JsonUtil.fromJson(moduleInfoObj, ModuleInfo.class);

        PYModule pyModule = new PYModule();
        // module name
        pyModule.setName(moduleInfo.getModule_name());
        // module clzList
        List<PYClass> pyModuleClassList = new ArrayList<>();
        for (ClassCollection moduleClasses : moduleInfo.getClasses()) {
            PYClass pyClass = new PYClass();
            pyClass.setName(moduleClasses.getClass_name());
            pyClass.setSig(moduleClasses.getClass_sig().getInit_sig());
            // module class function
            List<PYMethod> classFuncs = new ArrayList<>();
            for (FuncCollection func : moduleClasses.getClass_func()) {
                PYMethod pyMethod = new PYMethod();
                pyMethod.setName(func.getFunc_name());
                pyMethod.setSig(func.getFunc_sig());
                pyMethod.setClassFunction(true);
                classFuncs.add(pyMethod);
            }
            pyClass.setFuncs(classFuncs);
            // module class method ClassMethod decorate
            List<PYMethod> classMethods = new ArrayList<>();
            for (MethodCollection method : moduleClasses.getClass_method()) {
                PYMethod pyMethod = new PYMethod();
                pyMethod.setName(method.getMethod_name());
                pyMethod.setSig(method.getMethod_sig());
                pyMethod.setClassMethod(true);
                classMethods.add(pyMethod);
            }
            pyClass.setMethods(classMethods);

            pyModuleClassList.add(pyClass);
        }
        pyModule.setClzList(pyModuleClassList);

        // module func
        List<PYMethod> pyModuleFuncList = new ArrayList<>();
        for (FuncCollection moduleFunc : moduleInfo.getModule_func()) {
            PYMethod pyMethod = new PYMethod();
            pyMethod.setName(moduleFunc.getFunc_name());
            pyMethod.setSig(moduleFunc.getFunc_sig());
            pyMethod.setModuleMethod(true);
            pyModuleFuncList.add(pyMethod);
        }
        pyModule.setFuncList(pyModuleFuncList);
        return pyModule;
    }

    public PYClass classInfo(String classFullName) {
        String urlPath = "/api/moduleClassInfo/";
        String httpResponse;

        PYYjsRequest pyYjsRequest = new PYYjsRequest();
        PYYjsParams pyYjsParams = new PYYjsParams();
        pyYjsParams.setModule_name(HttpExt.getModuleName(classFullName));
        pyYjsParams.setModule_class_name(HttpExt.getClassName(classFullName));
        pyYjsRequest.setYjsParams(pyYjsParams);
        System.out.println(JsonUtil.toJson(pyYjsRequest));
        httpResponse = HttpClient.post(ip, port, urlPath, JsonUtil.toJson(pyYjsRequest));

//        PYYjsResponse pyYjsResponse = JsonUtil.fromJson(httpResponse, PYYjsResponse.class);

        PYClass pyClass = new PYClass();

        ClassCollection classInfo;
        JsonObject pyResponse = JsonUtil.parseStringAsJsonObject(httpResponse);
        JsonObject classInfoObj =
                pyResponse.getAsJsonObject("yjsResult").getAsJsonObject("classInfo");
        classInfo = JsonUtil.fromJson(classInfoObj, ClassCollection.class);

        // 更新class function or method keyword name
        pyClass.setName(classInfo.getClass_name());
        pyClass.setSig(classInfo.getClass_sig().getInit_sig());

        // class function
        List<PYMethod> classFuncs = new ArrayList<>();
        for (FuncCollection func : classInfo.getClass_func()) {
            PYMethod pyMethod = new PYMethod();
            pyMethod.setName(func.getFunc_name());
            pyMethod.setSig(func.getFunc_sig());
            pyMethod.setClassFunction(true);
            classFuncs.add(pyMethod);
        }
        pyClass.setFuncs(classFuncs);

        // class method ClassMethod修饰的方法
        List<PYMethod> classMethods = new ArrayList<>();
        for (MethodCollection method : classInfo.getClass_method()) {
            PYMethod pyMethod = new PYMethod();
            pyMethod.setName(method.getMethod_name());
            pyMethod.setSig(method.getMethod_sig());
            pyMethod.setClassMethod(true);
            classMethods.add(pyMethod);
        }
        pyClass.setMethods(classMethods);
        return pyClass;
    }

    public String objectInfo(String objectId) {
        String urlPath = "/api/objInfo/";
        String httpResponse;

        PYYjsRequest pyYjsRequest = new PYYjsRequest();
        PYYjsParams pyYjsParams = new PYYjsParams();
        pyYjsParams.setObjectId(objectId);
        pyYjsRequest.setYjsParams(pyYjsParams);
        httpResponse = HttpClient.post(ip, port, urlPath, JsonUtil.toJson(pyYjsRequest));
        return httpResponse;
    }

    public String invokeModuleFunc(
            String moduleFullName, String moduleFuncName, PYMethodParams PYMethodParams) {
        System.out.println("[PYEntry] invokeModuleFunc");
        String urlPath = "/api/invokeModuleFunc/";
        String httpResponse;

        PYYjsRequest pyYjsRequest = new PYYjsRequest();
        PYYjsParams pyYjsParams = new PYYjsParams();
        pyYjsParams.setPackage_name(HttpExt.getPackageName(moduleFullName));
        pyYjsParams.setModule_name(moduleFullName);
        pyYjsParams.setModule_func_name(moduleFuncName);
        pyYjsParams.setParams(PYMethodParams);
        pyYjsRequest.setYjsParams(pyYjsParams);
        httpResponse = HttpClient.post(ip, port, urlPath, JsonUtil.toJson(pyYjsRequest));
        JsonObject pyResponse = JsonUtil.parseStringAsJsonObject(httpResponse);
        JsonObject ysjResult = pyResponse.getAsJsonObject("yjsResult");
        return ysjResult.toString();
    }

    public String invokeObjectMethod(
            String objectId, String methodName, PYMethodParams PYMethodParams) {
        System.out.println("[PYEntry] invokeObjectGeneralMethod");
        String urlPath = "/api/invokeObjectGeneralMethod/";
        String httpResponse;

        PYYjsRequest pyYjsRequest = new PYYjsRequest();
        PYYjsParams pyYjsParams = new PYYjsParams();
        pyYjsParams.setObjectId(objectId);
        pyYjsParams.setObject_method_name(methodName);
        System.out.println(methodName);
        pyYjsParams.setParams(PYMethodParams);
        pyYjsRequest.setYjsParams(pyYjsParams);
        httpResponse = HttpClient.post(ip, port, urlPath, JsonUtil.toJson(pyYjsRequest));
        JsonObject pyResponse = JsonUtil.parseStringAsJsonObject(httpResponse);
        JsonObject ysjResult = pyResponse.getAsJsonObject("yjsResult");
        return ysjResult.toString();
    }

    public String invokeClzStaticMethod(String methodFullName, PYMethodParams PYMethodParams) {
        System.out.println("[PYEntry] invokeModuleClzStaticMethod");
        String urlPath = "/api/invokeModuleClzStaticMethod/";
        String httpResponse;

        PYYjsRequest pyYjsRequest = new PYYjsRequest();
        PYYjsParams pyYjsParams = new PYYjsParams();
        pyYjsParams.setPackage_name(HttpExt.getPackageName(methodFullName));
        pyYjsParams.setModule_name(
                HttpExt.getModuleName(HttpExt.getMethodClassFullName(methodFullName)));
        pyYjsParams.setModule_class_name(
                HttpExt.getClassName(HttpExt.getMethodClassFullName(methodFullName)));
        pyYjsParams.setModule_class_staticmethod_name(HttpExt.getMethodName(methodFullName));
        pyYjsParams.setParams(PYMethodParams);
        pyYjsRequest.setYjsParams(pyYjsParams);
        httpResponse = HttpClient.post(ip, port, urlPath, JsonUtil.toJson(pyYjsRequest));
        JsonObject pyResponse = JsonUtil.parseStringAsJsonObject(httpResponse);
        JsonObject ysjResult = pyResponse.getAsJsonObject("yjsResult");
        return ysjResult.toString();
    }

    public String invokeClzClassMethod(String methodFullName, PYMethodParams PYMethodParams) {
        System.out.println("[PYEntry] invokeClzClassMethod");
        String urlPath = "/api/invokeModuleClzClassMethod/";
        String httpResponse;

        PYYjsRequest pyYjsRequest = new PYYjsRequest();
        PYYjsParams pyYjsParams = new PYYjsParams();
        pyYjsParams.setPackage_name(HttpExt.getPackageName(methodFullName));
        pyYjsParams.setModule_name(
                HttpExt.getModuleName(HttpExt.getMethodClassFullName(methodFullName)));
        pyYjsParams.setModule_class_name(
                HttpExt.getClassName(HttpExt.getMethodClassFullName(methodFullName)));
        pyYjsParams.setModule_class_classmethod_name(HttpExt.getMethodName(methodFullName));
        System.out.println(HttpExt.getMethodName(methodFullName));
        pyYjsParams.setParams(PYMethodParams);
        pyYjsRequest.setYjsParams(pyYjsParams);
        System.out.println(JsonUtil.toJson(pyYjsRequest));
        httpResponse = HttpClient.post(ip, port, urlPath, JsonUtil.toJson(pyYjsRequest));
        JsonObject pyResponse = JsonUtil.parseStringAsJsonObject(httpResponse);
        JsonObject ysjResult = pyResponse.getAsJsonObject("yjsResult");
        return ysjResult.toString();
    }

    class MyCollections {
        List<String> packages;
        List<String> modules;

        public List<String> getPackages() {
            return packages;
        }

        public List<String> getModules() {
            return modules;
        }
    }

    class PackageInfo {
        String package_name;
        List<String> module_names;

        public String getPackage_name() {
            return package_name;
        }

        public List<String> getModule_names() {
            return module_names;
        }
    }

    class MethodCollection {
        String method_name;
        String method_sig;

        public String getMethod_name() {
            return method_name;
        }

        public String getMethod_sig() {
            return method_sig;
        }
    }

    class FuncCollection {
        String func_name;
        String func_sig;

        public String getFunc_name() {
            return func_name;
        }

        public String getFunc_sig() {
            return func_sig;
        }
    }

    class ClassInitSig {
        String init_sig;

        public String getInit_sig() {
            return init_sig;
        }
    }

    class ClassCollection {
        String class_name;
        ClassInitSig class_sig;
        List<MethodCollection> class_method;
        List<FuncCollection> class_func;

        public String getClass_name() {
            return class_name;
        }

        public ClassInitSig getClass_sig() {
            return class_sig;
        }

        public List<MethodCollection> getClass_method() {
            return class_method;
        }

        public List<FuncCollection> getClass_func() {
            return class_func;
        }
    }

    class ModuleInfo {
        String module_name;
        List<FuncCollection> module_func;
        List<ClassCollection> classes;

        public String getModule_name() {
            return module_name;
        }

        public List<FuncCollection> getModule_func() {
            return module_func;
        }

        public List<ClassCollection> getClasses() {
            return classes;
        }
    }
}
