package com.xydl.cac.iec;

import com.beanit.iec61850bean.*;
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.socket.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;

@Slf4j
public class IecClient implements ClientEventListener {

    public IcdIed ied;
    int[] title;
    ClientSap clientSap = new ClientSap();
    ClientAssociation clientAssociation = null;
    ServerModel serverModel;

    public boolean keep = false;
    public boolean connected = false;
    private WebSocketServer webSocketServer = null;
    public int retry = 0;
    public int seconds = 0;
    private boolean inRetry = false;
    public Date lastReportTime;

    public IecClient() {
    }

    public IecClient(WebSocketServer _webSocketServer, boolean warning) {
        if (warning) {
            webSocketServer = _webSocketServer;
        }
    }

    public void init(IcdIed _ied, String xml) throws Exception {
        InputStream in = IOUtils.toInputStream(xml, StandardCharsets.UTF_8);
        this.init(_ied, in);
    }

    public void init(IcdIed _ied, InputStream in) throws Exception {
        ied = _ied;
        serverModel = SclParser.parse(in).get(0);
    }

    public void connect() throws Exception {
        title = new int[]{1, 3, 9999, 33};
        if (StringUtils.isNotBlank(ied.getApTitle())) {
            String[] strs = ied.getApTitle().replaceAll(" ", ",").split(",");
            if (strs.length == 4) {
                title[0] = Integer.parseInt(strs[0]);
                title[1] = Integer.parseInt(strs[1]);
                title[2] = Integer.parseInt(strs[2]);
                title[3] = Integer.parseInt(strs[3]);
            } else if (strs.length == 5) {
                title = new int[]{1, 3, 9999, 33, 1};
                title[0] = Integer.parseInt(strs[0]);
                title[1] = Integer.parseInt(strs[1]);
                title[2] = Integer.parseInt(strs[2]);
                title[3] = Integer.parseInt(strs[3]);
                title[4] = Integer.parseInt(strs[4]);
            }
        }
        clientSap.setTSelRemote(new byte[]{0, 1});
        clientSap.setTSelLocal(new byte[]{0, 0});
        clientSap.setApTitleCalled(title);

        clientAssociation = clientSap.associate(InetAddress.getByName(ied.getIp()), ied.getPort(), null, this);
        clientAssociation.setServerModel(serverModel);
        connected = true;
    }

    public void disconnect() {
        try {
            connected = false;
            clientAssociation.disconnect();
            clientAssociation = null;
        } catch (Exception ignore) {
        }
    }

    private void reconnect() throws Exception {
        if (StaticVariable.shutdown == 1) {
            return;
        }
        retry++;
        seconds = 0;
        clientAssociation = clientSap.associate(InetAddress.getByName(ied.getIp()), ied.getPort(), null, this);
        clientAssociation.setServerModel(serverModel);
        log.info("61850订阅断线重连成功, ied=" + ied.getName() + ", ip=" + ied.getIp() + ", port=" + ied.getPort());
        connected = true;
        retry = 0;
    }

    public String getValue(String paramindex, String fc) throws Exception {
        BasicDataAttribute node = this.getNode(paramindex, fc);
        if (node != null) {
            return node.getValueString();
        }
        return null;
    }

    public BasicDataAttribute getNode(String paramindex, String fc) throws Exception {
        FcModelNode node = (FcModelNode) serverModel.findModelNode(paramindex, Fc.valueOf(fc));
        if (node == null) {
            throw new BusinessException("icd文件里未找到该节点, " + fc + " " + paramindex);
        }
        clientAssociation.getDataValues(node);
        if (node instanceof BasicDataAttribute) {
            return (BasicDataAttribute) node;
        }
        return null;
    }

    public void enableReporting() throws Exception {
        if (!CollectionUtils.isEmpty(serverModel.getUrcbs())) {
            for (Urcb urcb : serverModel.getUrcbs()) {
                clientAssociation.enableReporting(urcb);
            }
        }
        if (!CollectionUtils.isEmpty(serverModel.getBrcbs())) {
            for (Brcb brcb : serverModel.getBrcbs()) {
                clientAssociation.enableReporting(brcb);
            }
        }
        log.info("61850订阅成功, ied=" + ied.getName() + ", ip=" + ied.getIp() + ", port=" + ied.getPort());
    }

    public void disableReporting() {
        if (!CollectionUtils.isEmpty(serverModel.getUrcbs())) {
            try {
                for (Urcb urcb : serverModel.getUrcbs()) {
                    clientAssociation.disableReporting(urcb);
                }
            } catch (Exception ignore) {
            }
        }
        if (!CollectionUtils.isEmpty(serverModel.getBrcbs())) {
            try {
                for (Brcb brcb : serverModel.getBrcbs()) {
                    clientAssociation.disableReporting(brcb);
                }
            } catch (Exception ignore) {
            }
        }
        if (keep) {
            log.warn("61850订阅已断开, ied=" + ied.getName() + ", ip=" + ied.getIp() + ", port=" + ied.getPort());
        } else {
            log.info("61850停止订阅, ied=" + ied.getName() + ", ip=" + ied.getIp() + ", port=" + ied.getPort());
        }
    }

    public List<FileInformation> listFile(String path) throws Exception {
        List<FileInformation> list = clientAssociation.getFileDirectory(path);
        return list;
    }

    public void getFile(String remotePath, String localPath, Integer todel) throws Exception {
        String tmpFile = localPath + ".tmp";
        GetFileAction gfa = new GetFileAction(tmpFile);
        clientAssociation.getFile(remotePath, gfa);
        if (todel != null && todel == Constants.TRUE) {
            clientAssociation.deleteFile(remotePath);
        }
        File file = new File(tmpFile);
        file.renameTo(new File(localPath));
    }

    @Override
    public void newReport(Report report) {
        if (report != null) {
            lastReportTime = new Date();
            RealTimeDataService.processReport(report);
        }
    }

    @Override
    public void associationClosed(IOException e) {
        if (keep && !inRetry) {
            inRetry = true;
            retry = 0;
            seconds = 0;
            this.disableReporting();
            this.disconnect();
            while (!connected && StaticVariable.shutdown == 0) {
                try {
                    Thread.sleep(1000);
                    seconds++;
                    if (retry < 10 && seconds > 60) {
                        this.reconnect();
                    } else if (seconds > 60 * 60) {
                        this.reconnect();
                    }
                } catch (Exception ignore) {
                    String err = "61850订阅断线重连失败" + retry + "次, ied="
                            + ied.getName() + ", ip=" + ied.getIp() + ", port=" + ied.getPort();
                    log.warn(err);
                    if (webSocketServer != null && retry >= 10) {
                        webSocketServer.sendMessage(err, null);
                    }
                }
            }
            inRetry = false;
        }
    }

    public static void main(String[] args) {
        IecClient iecClient = new IecClient();
        try {
            IcdIed ied = IcdIed.builder()
                    .ip("192.168.1.17")
                    .port(102)
                    .apTitle("1 3 9999 33")
                    .build();
            iecClient.init(ied, new FileInputStream("C:/资料/om.SCD"));
            iecClient.connect();
            String str = iecClient.getValue("OMDLMONT/SPDC1.MaxDsch.mag.f", "MX");
            System.out.println(str);
            str = iecClient.getValue("OMDLMONT/SPDC1.MaxDsch.t", "MX");
            System.out.println(str);
            List<FileInformation> list = iecClient.listFile("/fdata");
            iecClient.getFile("/fdata/web/comtrade/1/18/6/20241021_113736_703_0_0.dat", "/eqmid/20241021_113736_703_0_0.dat", 1);

        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            iecClient.disconnect();
        }
    }
}
