package org.ts.ddcs.Worker;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.ts.ddcs.DataItemTemplate.DataItemTemplateInterface;
import org.ts.ddcs.Entity.RelayDatagramRecordEntity;
import org.ts.ddcs.log.LogHelper;
import org.ts.ddcs.Service.RtuService;
import org.ts.ddcs.Service.WqService;
import org.ts.ddcs.Tool.BytesHelp;
import org.ts.ddcs.Tool.IForestCheckHelp;
import org.ts.ddcs.Tool.StringUtil;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.client.RestTemplate;

import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.Socket;
import java.net.URI;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Callable;

@SuppressWarnings("all")
public class DataGramSzy2012RelayProcess implements Callable<Integer> {

    private String _rtuCode;
    private String _ip;
    private String _datagram;
    private Date _collectTime;
    private Date _pickTime;
    private HashMap<String, DataItemTemplateInterface> dataItem;

    DataGramSzy2012RelayProcess(String rtuCode, Date collectTime, Date pickTime, String ip, String datagram) {
        this._rtuCode = rtuCode;
        this._collectTime = collectTime;
        this._pickTime = pickTime;
        this._ip = ip;
        this._datagram = datagram;
    }

//    private boolean CheckCorrect(JSONObject body) {
//        JSONObject result = null;
//        try {
//            RestTemplate restTemplate = new RestTemplate();
//            result = restTemplate.postForObject(new URI("http://172.9.0.182:9000/szy/check/"), body, JSONObject.class);
//            if (result != null) {
//                LogHelper.info("DataGramSzy2012RelayProcess correct  speed " + result.toJSONString());
//                if (result.getString("code").equals("0")) {
//                    String checkResult = result.getString("checkResult");
//                    if (null != checkResult && checkResult.equals("1")) {
//                        return true;
//                    }
//                }
//            }
//        } catch (Exception e) {
//            LogHelper.error(e);
//        }
//        return false;
//    }

    private boolean CheckCorrect(String rtuCode, Date tm, double predictValue) {
        try {
            WqService wqService = DataProcessHolder.newInstance().dataProcess.wqService;
            int ext = 300;
            int trees = 10;
            int samples = 256;
            int treeLevel = 8;
            List<Double> dataValue = new ArrayList<>();
            int limit = trees * samples / ext;
            if (limit * ext < trees * samples) {
                limit += 1;
            }
            limit += 1;

            SimpleDateFormat timeFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String time = timeFormat2.format(tm);
            List<Map<String, Object>> list = wqService.QuerySpeedListByTm(rtuCode, limit, time);
            if (list.size() * ext < trees * samples) {
                return false;
            }
            List<Double> extList = new ArrayList<>();
            Map<String, Object> prePoint = list.get(list.size() - 1);
            double preValue = Double.parseDouble((String) prePoint.get("SPEED"));
            for (int i = list.size() - 1; i > 0; i--) {
                Map<String, Object> point = list.get(i - 1);
                double value = Double.parseDouble((String) point.get("SPEED"));
                double addValue = (value - preValue) / ext;
                extList.add(preValue);
                for (int j = 1; j < ext; j++) {
                    preValue += addValue;
                    extList.add(preValue);
                }
                preValue = value;
            }

            for (int i = 0; i < trees * samples; i++) {
                dataValue.add(extList.get(extList.size() - 1 - i));
            }

            Collections.shuffle(dataValue);

            List<Double> shuffledatas = new ArrayList<>();
            for (int n = dataValue.size(); n > 0; n--) {
                shuffledatas.add(dataValue.get(n - 1));
            }
            Collections.shuffle(shuffledatas);

            int check = IForestCheckHelp.IForestPredict(shuffledatas, predictValue, trees, samples, treeLevel);
            if (check == IForestCheckHelp.PREDICT_OUTLIERS) {
                return true;
            }
        } catch (Exception e) {
            LogHelper.error(e);
        }
        return false;
    }

    @Override
    public Integer call() {
        RelayDatagramRecordEntity relayRecord = new RelayDatagramRecordEntity();
        try {
            SimpleDateFormat timeFormat = new SimpleDateFormat("yyyyMMddHHmmss");
            SimpleDateFormat timeFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

            relayRecord.setDatagram(this._datagram);
            relayRecord.setRtuCode(this._rtuCode);
            relayRecord.setRelayIp(this._ip);
            relayRecord.setCollectTime(timeFormat2.format(this._collectTime));
            relayRecord.setPickTime(timeFormat2.format(this._pickTime));

            Socket socket = null;
            OutputStream outputStream = null;
            InputStream inputStream = null;
            LogHelper.info("DataGramSzy2012RelayProcess " + this._rtuCode + " " + this._ip + " " + this._datagram);
            String[] ips = this._ip.split(":");
            if (null == ips || ips.length != 2) {
                LogHelper.info("DataGramSzy2012RelayProcess ip error " + this._rtuCode + " " + this._ip);
                relayRecord.setRelayError("ip error ");
                return 0;
            }
            String host = ips[0];
            int port = Integer.parseInt(ips[1]);
            socket = new Socket(host, port);
            outputStream = socket.getOutputStream();
            byte[] sendBuff = BytesHelp.hexStr2Bytes(this._datagram);

            boolean isCorrect = true;

            RedisTemplate redisTemplate = DataProcessHolder.newInstance().dataProcess.redisTemplate;
            HashOperations<String, String, String> opsHash = redisTemplate.opsForHash();
            if (opsHash.hasKey(this._rtuCode, "correctValue")) {
                String correctValue = opsHash.get(this._rtuCode, "correctValue");
                if (null != correctValue && correctValue.equals("0")) {
                    isCorrect = false;
                }
            }
            if (isCorrect) {
                String rtuCode = this._datagram.substring("681A68B3".length(), "681A68B3".length() + 10);
                /**判断是否自报流速*/
                String ca = this._datagram.substring("681A68".length(), "681A68".length() + 2);
                String afn = this._datagram.substring("681A68B33232000100".length(), "681A68B33232000100".length() + 2);
                if (ca.equals("B4") && afn.equals("C0")) {

                    byte[] valueContentBuff = new byte[0];
                    byte[] dataBuff = BytesHelp.hexStr2Bytes(this._datagram);
                    int dataLen = dataBuff.length - 3 - 1 - 5 - 1 - 5 - 4 - 2;
                    if (dataLen >= 3) {
                        int count = 0;
                        while (true) {
                            if (count >= dataLen) {
                                break;
                            }
                            byte[] buff = BytesHelp.subBytes(dataBuff, 3 + 1 + 5 + 1 + count, 3);
                            byte tmp = buff[0];
                            buff[0] = buff[2];
                            buff[2] = tmp;
                            String value = BytesHelp.byte2HexStr(buff);
                            if (!value.equals("FFFFFF")) {
                                String flag = value.substring(0, 1);
                                String v1 = value.substring(1, 2);
                                String v2 = value.substring(2, 3);
                                String v3 = value.substring(3, 4);
                                String v4 = value.substring(4, 5);
                                String v5 = value.substring(5, 6);
                                value = "";
                                if (flag.equals("F")) {
                                    value += "-";
                                }
                                value += StringUtil.getPrettyNumber(v1 + v2 + "." + v3 + v4 + v5);
                                double checkValue = Double.parseDouble(value);

                                /**观测时间*/

                                String dt = timeFormat.format(new Date());
                                byte[] tm = BytesHelp.subBytes(dataBuff, dataBuff.length - 2 - 5, 5);
                                byte tmtmp = tm[0];
                                tm[0] = tm[4];
                                tm[4] = tmtmp;
                                tmtmp = tm[1];
                                tm[1] = tm[3];
                                tm[3] = tmtmp;
                                String collectTime = dt.substring(0, 4) + BytesHelp.byte2HexStr(tm);

                                WqService wqService = DataProcessHolder.newInstance().dataProcess.wqService;
                                ///  int minValue = 5 * 64;
                                //  List<Map<String, Object>> speedList = wqService.QuerySpeedList(rtuCode, minValue);
                                //    if (null != speedList && speedList.size() == minValue) {
//                                    JSONArray baseValues = new JSONArray();
//                                    for (Map<String, Object> m : speedList) {
//                                        baseValues.add(Double.parseDouble((String) m.get("SPEED")));
//                                    }

                                //  JSONObject body = new JSONObject();
                                //  body.put("checkValue", checkValue);
                                ///   body.put("baseValues", baseValues);


                                if (this.CheckCorrect(rtuCode, this._collectTime, checkValue)) {
                                    double correctValue = 0.0;
                                    List<Map<String, Object>> correctSpeeds = wqService.QueryCorrectSpeedList(rtuCode, timeFormat2.format(new Date(timeFormat.parse(collectTime).getTime())), 1);
                                    if (null != correctSpeeds && correctSpeeds.size() == 1) {
//                                        double sumV = 0.0;
//                                        for (Map<String, Object> c : correctSpeeds) {
//                                            double csv = Double.parseDouble((String) c.get("SPEED_CORRECT"));
//                                            sumV += csv;
//                                        }
//                                        correctValue = sumV / correctSpeeds.size();
                                        Map<String, Object> c = correctSpeeds.get(0);
                                        correctValue = Double.parseDouble((String) c.get("SPEED_CORRECT"));
                                    } else {
//                                        double sumV = 0.0;
//                                        for (int i = 0; i < 12; i++) {
//                                            sumV += baseValues.getDouble(i);
//                                        }
//                                        correctValue = sumV / 12.0;
                                        correctValue = checkValue / 2;
                                    }
                                    BigDecimal intv = new BigDecimal(correctValue);
                                    intv = intv.setScale(3, BigDecimal.ROUND_DOWN);
                                    wqService.UpdateSpeedValue(rtuCode, new Timestamp(timeFormat.parse(collectTime).getTime()), intv.toPlainString());
                                    String reportValueStr = intv.toPlainString();
                                    String[] reportValueStrList = reportValueStr.split("[.]");
                                    String highValue = "00";
                                    String lowValue = "000";
                                    if (reportValueStrList.length == 2) {
                                        highValue = reportValueStrList[0];
                                        lowValue = reportValueStrList[1];
                                    } else {
                                        highValue = reportValueStrList[0];
                                    }
                                    highValue = String.format("%02d", Integer.parseInt(highValue));
                                    String corvv = "0" + highValue + lowValue;
                                    byte[] corvvbuff = BytesHelp.hexStr2Bytes(corvv);
                                    byte corvvtmp = corvvbuff[0];
                                    corvvbuff[0] = corvvbuff[2];
                                    corvvbuff[2] = corvvtmp;
                                    String corvvHex = BytesHelp.byte2HexStr(corvvbuff);
                                    valueContentBuff = BytesHelp.arrayApend(valueContentBuff, BytesHelp.hexStr2Bytes(corvvHex));
                                    LogHelper.info(rtuCode + "relay " + "value correct" + intv.toPlainString());
                                } else {
                                    byte[] databuff = BytesHelp.subBytes(dataBuff, 3 + 1 + 5 + 1 + count, 3);
                                    valueContentBuff = BytesHelp.arrayApend(valueContentBuff, databuff);
                                    BigDecimal intv = new BigDecimal(checkValue);
                                    intv = intv.setScale(3, BigDecimal.ROUND_DOWN);
                                    wqService.UpdateSpeedValue(rtuCode, new Timestamp(timeFormat.parse(collectTime).getTime()), intv.toPlainString());
                                    LogHelper.info(rtuCode + "relay " + "value dont not need   to  correct " + intv.toPlainString());
                                }


//                                } else {
//
//                                    byte[] databuff = BytesHelp.subBytes(dataBuff, 3 + 1 + 5 + 1 + count, 3);
//                                    valueContentBuff = BytesHelp.arrayApend(valueContentBuff, databuff);
//                                    BigDecimal intv = new BigDecimal(checkValue);
//                                    intv = intv.setScale(3, BigDecimal.ROUND_DOWN);
//                                    wqService.UpdateSpeedValue(rtuCode, new Timestamp(timeFormat.parse(collectTime).getTime()), intv.toPlainString());
//
//                                    LogHelper.info(rtuCode + "relay " + "value no more data to  correct " + intv.toPlainString());
//                                }
                            } else {
                                byte[] databuff = BytesHelp.subBytes(dataBuff, 3 + 1 + 5 + 1 + count, 3);
                                valueContentBuff = BytesHelp.arrayApend(valueContentBuff, databuff);
                                LogHelper.info(rtuCode + "relay " + "value FFFFFF");
                            }
                            count += 3;
                        }
                    }
                    /**重新生成报文*/
                    String crcstr = this._datagram.substring("681A68".length(), "681A68".length() + "B33232000100C0".length());
                    if (valueContentBuff.length > 0) {
                        crcstr += BytesHelp.byte2HexStr(valueContentBuff);
                    }
                    crcstr += this._datagram.substring(this._datagram.length() - 11 * 2, this._datagram.length() - 2 * 2);
                    byte[] crcstrbuff = BytesHelp.hexStr2Bytes(crcstr);
                    byte[] crc = new byte[1]; //系统计算的校验码
                    crc[0] = BytesHelp.GetCRCByte(crcstrbuff);

                    byte[] lenbuff = new byte[1];
                    lenbuff[0] = (byte) crcstrbuff.length;

                    String datagramstr = "68" + BytesHelp.byte2HexStr(lenbuff) + "68" + crcstr + BytesHelp.byte2HexStr(crc) + "16";

                    LogHelper.info(rtuCode + " correct datagram" + datagramstr);
                    sendBuff = BytesHelp.hexStr2Bytes(datagramstr);

                    relayRecord.setDatagramProcess(datagramstr);

                }
            } else {
                LogHelper.info(_rtuCode + " not correct");
            }
            //发送报文数据
            outputStream.write(sendBuff);
            //记录报文发送时间
            relayRecord.setRelayTime(timeFormat2.format(new Date()));
            Thread.sleep(500);
            inputStream = socket.getInputStream();

            if (sendBuff[10] == (byte) 0x2f) {
                LogHelper.info("DataGramSzy2012RelayProcess  链路报 无回报 " + this._rtuCode);
                relayRecord.setRelayResult("0");
                relayRecord.setBackTime(timeFormat.format(new Date()));
            } else {

                byte[] buff = new byte[1024];
                int len = 0;
                byte[] bytes = new byte[1];
                int count = 0;
                int timeout = 0;
                int packlen = 0;
                int step = 0;
                while (true) {
                    int read = inputStream.available();
                    if (read <= 0) {
                        if (timeout > 10000) {
                            break;
                        }
                        Thread.sleep(500);
                        timeout += 500;
                    } else {
                        len = inputStream.read(bytes, 0, 1);
                        if (len != -1) {
                            if (len > 0) {
                                System.arraycopy(bytes, 0, buff, count, len);
                                count += len;
                                if (step == 0) {

                                    if (count == 3) {
                                        if (buff[0] == (byte) 0x68 && buff[2] == (byte) 0x68) {

                                            packlen = 5 + (int) buff[1];
                                            step = 1;
                                        } else {
                                            buff[0] = buff[1];
                                            buff[1] = buff[2];
                                            count -= 1;
                                        }
                                    }

                                } else {

                                    if (count == packlen) {
                                        break;
                                    }
                                }
                            }

                        } else {
                            break;
                        }
                    }
                }

                if (count > 0 && count == packlen) {
                    /**收到回报*/
                    relayRecord.setBackTime(timeFormat2.format(new Date()));
                    relayRecord.setDatagramBack(BytesHelp.byte2HexStr(buff, 0, count));

                    LogHelper.info("relay datagram back " + this._rtuCode + " " + relayRecord.getDatagramBack());
                    if (packlen > 5) {
                        byte[] crc = new byte[1];
                        crc[0] = BytesHelp.GetCRCByte(BytesHelp.subBytes(buff, 3, packlen - 5));
                        byte[] baowenCrc = BytesHelp.subBytes(buff, packlen - 2, 1);

                        if (crc[0] != baowenCrc[0]) {
                            relayRecord.setRelayResult("1");
                            LogHelper.info("DataGramSzy2012RelayProcess 校验失败 " + this._rtuCode + " CHECK CRC=" + BytesHelp.byte2HexStr(crc) + "-PACKET CRC=" + BytesHelp.byte2HexStr(baowenCrc));
                        } else {
                            relayRecord.setRelayResult("0");
                            LogHelper.info("DataGramSzy2012RelayProcess 校验成功 " + this._rtuCode + " CHECK CRC=" + BytesHelp.byte2HexStr(crc) + "-PACKET CRC=" + BytesHelp.byte2HexStr(baowenCrc));
                        }
                    }
                } else {
                    LogHelper.info("relay datagram dont get back " + this._rtuCode);
                    relayRecord.setRelayError("relay datagram dont get back");
                }

            }

            Thread.sleep(5000);
            outputStream.close();
            inputStream.close();
            socket.close();
        } catch (Exception e) {

            LogHelper.info("relay datagram error " + this._rtuCode);
            relayRecord.setRelayResult("1");
            relayRecord.setRelayError("relay datagram system error happen!");
            LogHelper.error(e);
        } finally {
            //记录转发信息
            String jsonStr = JSONObject.toJSONString(relayRecord);
            LogHelper.info("relay datagram record " + jsonStr);
            RtuService rtuService = DataProcessHolder.newInstance().dataProcess.rtuService;
            rtuService.SaveRelayDatagramInfo(relayRecord);
        }
        return 0;
    }
}
