package com.judong.chuanyiserver.util;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig;
import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
import org.eclipse.milo.opcua.sdk.client.nodes.UaNode;
import org.eclipse.milo.opcua.stack.client.DiscoveryClient;
import org.eclipse.milo.opcua.stack.core.Identifiers;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
import org.jinterop.dcom.common.JIException;
import org.openscada.opc.lib.da.Server;
import org.openscada.opc.lib.da.browser.Branch;
import org.openscada.opc.lib.da.browser.TreeBrowser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Predicate;

@Slf4j
public class OpcServerUaUtil {

    private static final String certPath = "C:/Users/Administrator/Desktop/";
    private static final String endpointUrl = "opc.tcp://192.168.0.252:49322";
//    private static final String endpointUrl = "opc.tcp://192.168.0.252:37800";

    /**
     * 创建OPC UA客户端
     *
     * @return
     */
    public static OpcUaClient createClient() throws Exception {
        Path securityTempDir = Paths.get(certPath, "security");

        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            log.info("无法创建安全目录: " + securityTempDir);
            return null;
        }
        KeyStoreLoader keyStoreLoader = new KeyStoreLoader();
        KeyStoreLoader loader = keyStoreLoader.load(securityTempDir);
        // 搜索OPC节点
        List<EndpointDescription> endpoints = null;
        try {
            endpoints = DiscoveryClient.getEndpoints(endpointUrl).get();
        } catch (Throwable e) {
            String discoveryUrl = endpointUrl;
            if (!discoveryUrl.endsWith("/")) {
                discoveryUrl += "/";
            }
            discoveryUrl += "discovery";
            endpoints = DiscoveryClient.getEndpoints(discoveryUrl).get();
        }
        EndpointDescription endpoint = endpoints.stream()
                .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri())).filter(endpointFilter())
                .findFirst().orElseThrow(() -> new Exception("no desired endpoints returned"));
        OpcUaClientConfig config = OpcUaClientConfig.builder()
                .setApplicationName(LocalizedText.english("my"))
                .setApplicationUri("urn:Jellyleo:UnifiedAutomation:UaExpert@Jellyleo")
                .setCertificate(loader.getClientCertificate()).setKeyPair(loader.getClientKeyPair())
                .setEndpoint(endpoint)
//				.setIdentityProvider(new UsernameProvider("jellyleo", "123456"))
                .setIdentityProvider(new AnonymousProvider()) // 匿名验证
                .setRequestTimeout(Unsigned.uint(5000)).build();

        return OpcUaClient.create(config);
    }

    private static Predicate<EndpointDescription> endpointFilter() {
        return e -> true;
    }

    public static CompletableFuture<OpcUaClient> getFuture() {
        return new CompletableFuture<>();
    }

    public static List<JSONObject> generOpcUaTree(OpcUaClient client, UaNode uaNode) throws UaException, ExecutionException, InterruptedException {
        if(Blank.isEmpty(client)){
            return null;
        }
        List<JSONObject> jsonList = new ArrayList<>();
        List<? extends UaNode> nodes;
        if (uaNode == null) {
            nodes = client.getAddressSpace().browseNodes(Identifiers.ObjectsFolder);
        } else {
            nodes = client.getAddressSpace().browseNodes(uaNode);
        }
        for (UaNode nd : nodes) {
            if(Blank.isEmpty(nd)){
                continue;
            }
            //排除系统行性节点，这些系统性节点名称一般都是以"_"开头
            if (Objects.requireNonNull(nd.getBrowseName().getName()).contains("_")) {
//                continue;
            }
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("label", nd.getBrowseName().getName());
//            jsonObject.put("value",client.readValue(0.0, TimestampsToReturn.Neither, nd.getNodeId()).get());
            jsonObject.put("children", generOpcUaTree(client, nd));
            jsonList.add(jsonObject);
        }
        return jsonList;
    }

//    private  void browseNode(OpcUaClient client, UaNode uaNode) throws Exception {
//        List<? extends UaNode> nodes;
//        if (uaNode == null) {
//			nodes = client.getAddressSpace().browseNodes(Identifiers.ObjectsFolder);//从根目录
////            nodes = client.getAddressSpace().browseNodes(new NodeId(2,"my"));//指定目录
//        } else {
//            nodes = client.getAddressSpace().browseNodes(uaNode);
//        }
//        if (ObjectUtils.isEmpty(nodes)) {
//            return;
//        }
//        for (UaNode nd : nodes) {
//            if (nd.getBrowseName().getName().contains("test")) {
//                browseNode(client, nd);
//            }
//            if (nd.getBrowseName().getName().contains("name")) {
//                Object identifier = nd.getNodeId().getIdentifier();
//                String s = identifier.toString();
//
//                nodeNames.add(s);
//            }
////			//排除系统行性节点，这些系统性节点名称一般都是以"_"开头
////			if (Objects.requireNonNull(nd.getBrowseName().getName()).contains("_")) {
////				continue;
////			}
//        }
//        System.err.println();
//    }

}
