package com.xydl.cac.iec;

import com.beanit.iec61850bean.*;
import com.xydl.cac.entity.IcdFile;
import com.xydl.cac.entity.IcdIed;
import com.xydl.cac.entity.constants.Constants;
import com.xydl.cac.exception.BusinessException;
import com.xydl.cac.model.StaticVariable;
import com.xydl.cac.repository.IcdFileRepository;
import com.xydl.cac.repository.IcdIedRepository;
import com.xydl.cac.socket.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.*;

@Service
@Slf4j
public class RealTimeDataService {
    @Value("${cac.61850.warning:true}")
    public boolean warning;
    @Resource
    IcdFileRepository fileRepository;
    @Resource
    IcdIedRepository iedRepository;
    @Resource
    WebSocketServer webSocketServer;

    public static boolean inDoing = false;

    @PostConstruct
    private void init() {
        List<IcdIed> list = iedRepository.findByStart(Constants.TRUE);
        if (!CollectionUtils.isEmpty(list)) {
            for (IcdIed ied : list) {
                try {
                    this.startCollect(ied);
                } catch (Exception ignore) {
                    NetErrorThread thread = new NetErrorThread(ied.getId());
                    thread.start();
                }
            }
        }
    }

    public void startCollect(IcdIed ied) throws BusinessException {
        Optional<IcdFile> optionalFile = fileRepository.findById(ied.getIcdFileId());
        if (!optionalFile.isPresent()) {
            throw new BusinessException("未找到该icd文件");
        }
        inDoing = true;
        IcdFile icdFile = optionalFile.get();
        IecClient iecClient = new IecClient(webSocketServer, warning);
        try {
            iecClient.init(ied, icdFile.getXml());
            iecClient.keep = true;
            StaticVariable.realTimeClientMap.put(ied.getId(), iecClient);
            iecClient.connect();
            iecClient.enableReporting();
        } catch (Exception ex) {
            String err = "61850订阅异常, ied=" + ied.getName() + ", ip=" + ied.getIp() + ", port=" + ied.getPort();
            log.error(err, ex);
            throw new BusinessException(err);
        } finally {
            inDoing = false;
        }
    }

    public void stopCollect(Integer iedId) {
        IecClient iecClient = StaticVariable.realTimeClientMap.get(iedId);
        if (iecClient != null) {
            StaticVariable.realTimeClientMap.remove(iedId);
            iecClient.keep = false;
            iecClient.disableReporting();
            iecClient.disconnect();
        }
    }

    @PreDestroy
    private void stop() {
        log.info("关闭61850订阅服务.");
        StaticVariable.shutdown = 1;
        List<Integer> idList = new ArrayList<>();
        Iterator<Integer> it = StaticVariable.realTimeClientMap.keySet().iterator();
        while (it.hasNext()) {
            idList.add(it.next());
        }
        for (Integer iedId : idList) {
            this.stopCollect(iedId);
        }
    }

    public static void processReport(Report report) {
        if (!CollectionUtils.isEmpty(report.getValues())) {
            for (FcModelNode node : report.getValues()) {
                try {
                    processNodeValue(node);
                } catch (Exception ignore) {
                }
            }
        }
    }

    private static void processNodeValue(FcModelNode node) {
        if (node instanceof FcDataObject) {
            FcDataObject fcnode = (FcDataObject) node;
            if (!CollectionUtils.isEmpty(fcnode.getChildren())) {
                for (ModelNode child : fcnode.getChildren()) {
                    if (child instanceof BasicDataAttribute) {
                        BasicDataAttribute bda = (BasicDataAttribute) child;
                        StaticVariable.updateServerNodeValue(bda);
                        StaticVariable.updateLastData(bda);
                    } else if (child instanceof ConstructedDataAttribute) {
                        ConstructedDataAttribute cda = (ConstructedDataAttribute) child;
                        if (!CollectionUtils.isEmpty(cda.getChildren())) {
                            for (ModelNode cchild : cda.getChildren()) {
                                if (cchild instanceof BasicDataAttribute) {
                                    BasicDataAttribute bda = (BasicDataAttribute) cchild;
                                    StaticVariable.updateServerNodeValue(bda);
                                    StaticVariable.updateLastData(bda);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

}
