package org.bdware.sc.server;

import com.google.gson.JsonElement;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.doip.audit.EndpointConfig;
import org.bdware.doip.audit.EndpointInfo;
import org.bdware.doip.audit.client.AuditIrpClient;
import org.bdware.doip.audit.config.TempConfigStorage;
import org.bdware.doip.endpoint.server.DoipListenerConfig;
import org.bdware.doip.endpoint.server.DoipServerImpl;
import org.bdware.doip.endpoint.server.DoipServiceInfo;
import org.bdware.doip.endpoint.server.StartServerCallback;
import org.bdware.sc.ContractProcess;
import org.bdware.sc.boundry.JavaScriptEntry;
import org.bdware.sc.crdt.SharableVarManager;
import org.bdware.sc.handler.DOOPRequestHandler;
import org.zz.gmhelper.SM2KeyPair;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class DoipLocalSingleton {
    static Logger LOGGER = LogManager.getLogger(DoipLocalSingleton.class);
    private static DoipServerImpl server;

    public static void main(String[] arg) throws InterruptedException {
        final int port = (arg.length == 0 ? 21042 : Integer.parseInt(arg[0]));

        Thread doipServerThread = new Thread() {
            @Override
            public void run() {
                try {
                    DoipLocalSingleton.run(port, null);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        doipServerThread.start();
    }

    public static int run(int port, JsonElement otherConfigs) throws InterruptedException {
        int i = -1;
        LOGGER.info("try to listener port:" + port);
        int j = 0;
        for (i = run("tcp://127.0.0.1:" + port++, otherConfigs); i < 0 && j < 3; j++) {
            LOGGER.info("try again to listener port:" + port);
            LOGGER.error("try again to listener port:" + port);
            System.out.println("try again to listener port:" + port);
            i = run("tcp://127.0.0.1:" + port++, otherConfigs);
        }
        return i;
    }

    public static int run(String doipAddr, JsonElement otherConfigs) throws InterruptedException {
        List<DoipListenerConfig> infos = new ArrayList<>();
        int port = -1;
        try {
            URI uri = new URI(doipAddr);
            port = uri.getPort();
            infos.add(new DoipListenerConfig(doipAddr, "2.1"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        SM2KeyPair keyPair = JavaScriptEntry.getKeyPair();
        String repoID = "bdtest/BDRepo/" + UUID.randomUUID().toString();
        String owner = ContractProcess.instance.getContract().getOwner();
        String repoType = "BDO";
        EndpointConfig config = null;
        try {
            if (otherConfigs != null && otherConfigs.isJsonObject()) {
                config = new TempConfigStorage(otherConfigs.toString()).loadAsEndpointConfig();
                if (otherConfigs.getAsJsonObject().has("repoID")) {
                    repoID = otherConfigs.getAsJsonObject().get("repoID").getAsString();
                }
                if (config.privateKey == null || config.publicKey == null) {
                    config.privateKey = keyPair.getPrivateKeyStr();
                    config.publicKey = keyPair.getPublicKeyStr();
                }
                if (config.routerURI != null && config.repoName != null) {
                    AuditIrpClient irpClient = new AuditIrpClient(config);
                    EndpointInfo endpointInfo = irpClient.getEndpointInfo();
                    repoID = endpointInfo.getDoId();
                    owner = endpointInfo.getPubKey();
                    infos.clear();
                    infos.add(new DoipListenerConfig(endpointInfo.getURI(), "2.1"));
                    port = new URI(endpointInfo.getURI()).getPort();
                }
            }
            DoipServiceInfo info = new DoipServiceInfo(repoID, owner, repoType, infos);
            server = new DoipServerImpl(info);
            DOOPRequestHandler handler = ContractProcess.instance.doopRequestHandler;
            server.setRequestCallback(handler);
            if (config != null)
                SharableVarManager.initSharableVarManager(info.id, config);
        } catch (Exception e) {
            e.printStackTrace();
        }
        ResultChecker checker = new ResultChecker();
        server.start(checker);
        checker.waitForResult(1000);
        if (checker.port > 0)
            return port;
        return -1;
    }

    static class ResultChecker implements StartServerCallback {
        int port = -2;

        @Override
        public void onSuccess(int i) {
            port = i;
            synchronized (this) {
                this.notify();
            }
        }

        public void waitForResult(long timeout) {
            synchronized (this) {
                try {
                    this.wait(timeout);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        @Override
        public void onException(Exception e) {
            port = -1;
            synchronized (this) {
                this.notify();
            }
        }
    }

}
