package com.unis.detectionnum.server.utils;

import com.google.common.collect.Lists;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import com.unis.detectionnum.common.vo.GetXYVO;
import com.unis.detectionnum.server.domain.ScanPointsDomain;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * 文件描述
 *
 * @author tongxi.xia
 * @date 2020年05月21日 13:31
 */
@Service
public class ScanUtils {

    private static final Logger log = LoggerFactory.getLogger(ScanUtils.class);

    @Autowired
    ScanPointsDomain pointsDomain;

    SocketUtils socketUtils = new SocketUtils();

    /**
     * 1.产生数据并处理，将数据入库，2.发送命令并处理返回的数据入库
     *
     * @param h                处理高度
     * @param smExtent         扫描长
     * @param smWide           扫描宽
     * @param houseLengthLeft  左边仓房长度
     * @param houseLengthRight 右边仓房长度
     * @param operateId        操作id
     * @param sbCode           设备编码
     * @param socket           cfgd仓房高度
     *                         zLXGD 装粮线高度
     *                         wd 融重
     * @throws IOException
     * @throws InterruptedException 返回本次扫描最高点
     */
    public double dataProcessingOne(double h, double smExtent, double smWide, double houseLengthRight, double houseLengthLeft, String operateId, String sbCode, Socket socket, double cfgd, double zLXGD, double wd) throws SocketTimeoutException, IOException, InterruptedException {
        //生成扫描的点数 并入库
        List<GetXYVO> getXYS = getMaxz(cfgd, smExtent, smWide, houseLengthRight, houseLengthLeft, operateId, sbCode, zLXGD);
        //发送指令获取第一次扫描的最高点z
        Double maxz = getMaxpz(getXYS, socket, sbCode, operateId, h, cfgd);
        return maxz;
    }

    /**
     * 解析ret,返回点数据（含X,y,z<z计算出来的>，可信度）
     * h代表仓房高度减去扫描高度；
     *
     * @param ret 返回的字符串
     */
    public Double analysisString(String ret, String sbCode, String operateId, double h, double CfHeight, GetXYVO getXYVO) {
        if (ret == null || ret.equals("")) {
            return null;
        }
        double AA, AB, AC, Aa, Ab, Ac;   //如上图,A舵机，大写ABC代表三个边，小写abc代表三个角。
        double BA, BB, BC, Ba, Bb, Bc;   //如上图,B舵机，大写ABC代表三个边，小写abc代表三个角。
        double AcTemp, BATemp;
        //获取返回的x,y,以及斜边距离z
        String[] return_str = ret.trim().split(" ", 14);
        String rets = return_str[7] + return_str[8] + return_str[9] + return_str[10];
        //获取 hypotenuse:斜边长；
        log.info("返回结果截取数据：" + rets);
        BA = DataMeasureUtils.mmToM(DataMeasureUtils.hexToInt(rets));
        //设备y值的弧度b舵机
        Bc = getXYVO.getPoint_radian(); //Bc
        //设备x,A舵机
        Ac = getXYVO.getPointx_radian(); //Ac
        //获取分区象限
        int region = getXYVO.getS_region();
        //仓房左边长度
        double houseLengthLeft = getXYVO.getHouseLengthLeft();
        double houseLengthRight = getXYVO.getHouseLengthRight();
        //仓房宽度
        double houseWide = getXYVO.getHouseWide();

        //判断是否打到墙上
        if (region == 1 || region == 3) {
            AB = houseLengthLeft - 0.5;
        } else {
            AB = houseLengthRight - 0.5;
        }
        AC = houseWide / 2 - 0.5;
        AcTemp = Math.atan(AB / AC) / (Math.PI / 180);  //转动方向1500+
        if (Ac > AcTemp)  //打到左墙面
        {
            log.info("888888888888888888888888888888888************************进入if打到左墙面");
            AA = AB / Math.sin(Math.toRadians(Ac));
            BB = AA;
            BATemp = BB / Math.sin(Math.toRadians(Bc));
        } else {  //打到下墙面
            log.info("888888888888888888888888888888888************************进入else打到下墙面");
            AA = AC / Math.cos(Math.toRadians(Ac));
            BB = AA;
            BATemp = BB / Math.sin(Math.toRadians(Bc));
        }
        //说明打到墙上了
        log.info("888888888888888888888888888888888****************************BA: " + BA + " BATemp:" + BATemp);
        if (BA >= BATemp) {
            log.info("*************************打到墙上了****************************BA: " + BA + " BATemp:" + BATemp);
            return null;//跳过，这个点不要了
        }
        double pointZ;
        if (((int) Bc == 0)) {
            log.info("00仓房高度：" + CfHeight + " 计算结果为：" + h);
            pointZ = (CfHeight - h);
        } else {
            double height = DataMeasureUtils.getHeight(BA, Bc);
            log.info("仓房高度：" + CfHeight + " 斜边长： " + BA + " y获取弧度: " + Bc + " 计算结果为：" + height);
            pointZ = CfHeight - height;
        }
        log.info("仓房高度减去计算扫描点的高度最终测量结果： " + pointZ);
        return pointZ;
    }

    /**
     * 根据x，y拼接命令
     *
     * @param getxy x，y集合
     * @return 命令集合
     */
    public List<String> concatOders(List<GetXYVO> getxy) {
        //定义初始化命令字符串
        String order = "";
        //初始化命令集合
        List<String> orders = new ArrayList();
        for (int i = 0; i < getxy.size(); i++) {
            String x = DataMeasureUtils.lowHigh((int) getxy.get(i).getPointX());
            String y = DataMeasureUtils.lowHigh((int) getxy.get(i).getPointY());

            order = CommonDataMeasure.HEAD + " " + CommonDataMeasure.SLAVE_ID + " " + CommonDataMeasure.CODE_ID + " " +
                    x + " " + y + " " + CommonDataMeasure.CHECK_CODE;
            orders.add(order);
        }
        return orders;
    }


    /**
     * 获取x y 入库x，y 并入库扫描记录
     *
     * @param extent           长
     * @param wide             宽
     * @param cfgd             高
     * @param houseLengthLeft  仓房左边长
     * @param houseLengthRight 仓房右边长
     * @return 返回x，y集合
     * h仓房高度,extent仓房长度,wide仓房宽度,extentAx,wideBy,scanStart,sbCode,times zLXGD装粮线高度
     */
    public List<GetXYVO> getMaxz(double cfgd, double extent,
                                 double wide, double houseLengthRight, double houseLengthLeft, String operateId, String sbCode, double zLXGD) {
        //扫描点xy入库
        List<GetXYVO> getScanPoints = DataMeasureUtils.getXYStepts1(cfgd, extent, wide, houseLengthRight, houseLengthLeft, zLXGD, operateId, sbCode);
        pointsDomain.insertPointsXOrY2(getScanPoints);

        return getScanPoints;
    }

    public Double getMaxpz(List<GetXYVO> getXYS, Socket socket, String sbCode, String operateId, double h, double cfgd) throws IOException, InterruptedException {
        //根据getXYS集合拼接命令
        List<String> odres = concatOders(getXYS);
        log.info("***第一次扫描指令数量***  " + odres.size() + " operateId:" + operateId);
        for (int i = 0; i < odres.size(); i++) {
            log.info("***第一次扫描指令***  " + odres.get(i));
        }
        int temp2 = 0;//记录扫描到墙上的点
        //发送命令集合并将返回值z及可信度入库
        for (int i = 0; i < odres.size(); i++) {
            //获取y轴
            GetXYVO getXYVO1 = getXYS.get(i);
            String id = getXYVO1.getId();
            log.info("***本次扫描发送指令***  " + odres.get(i));
            //发送十六进制的字符串并 返回十六进制的字符串
            String ret = socketUtils.sendRequest(socket, odres.get(i));
            if (StringUtils.isEmpty(ret) || ret.length() < CommonDataMeasure.RETURN_DATA) {
                log.info("读取设备数据超时" + odres.get(i));
                pointsDomain.deleteByXYP2(id);
                continue;
            }
//            Thread.sleep(1000);
            int temp = 0;
            if (ret.contains(CommonDataMeasure.BAD_DATA)) {
                for (int a = 0; a < 2; a++) {
                    //延长5秒
//                    Thread.sleep(1000);
                    ret = socketUtils.sendRequest(socket, (String) odres.get(i));
                    if (ret == null || ret.equals("") || ret.length() < CommonDataMeasure.RETURN_DATA) {
                        continue;
                    }
                    if (!ret.contains(CommonDataMeasure.BAD_DATA)) {
                        break;
                    }
                    temp++;
                }
            }
//            Thread.sleep(5000);
            //解析十六进制的字符串 获取，x，y，可信度 并计算z值
            if (ret == null || ret.equals("") || ret.contains(CommonDataMeasure.BAD_DATA) || ret.length() < CommonDataMeasure.RETURN_DATA) {
                pointsDomain.deleteByXYP2(id);
                continue;
            }
            Double aDouble = analysisString(ret, sbCode, operateId, h, cfgd, getXYVO1);
            if (temp >= 2 || aDouble == null) {
                temp2++;
                pointsDomain.deleteByXYP2(id);

            } else {
                //根据 x y update Z 可信度 将z 可信度 值入库
                GetXYVO getXYVO = new GetXYVO();
                getXYVO.setId(id);
                getXYVO.setPointZ(aDouble);
                pointsDomain.updateZ2(getXYVO);
            }
            //临时输出
            log.info("扫描第" + i + "次end" + ";命令" + odres.get(i));
        }
        System.out.println("总共扫到墙上多少次： " + temp2);
        double max = pointsDomain.selectByoperateIdMax(operateId);
        return max;
    }

    public void getMaxpz2(List<GetXYVO> getXYS, Socket socket, String sbCode, String operateId, double h, double cfgd) throws IOException, InterruptedException {
        List<String> odres = concatOders(getXYS);
        //发送命令集合并将返回值z及可信度入库
        log.info("***第二次扫描指令数量***  " + odres.size() + " operateId:" + operateId);
        for (int i = 0; i < odres.size(); i++) {
            log.info("***第二次扫描指令***  " + odres.get(i));
        }
        int temp2 = 0;//记录打到墙上的点
        for (int i = 0; i < odres.size(); i++) {
            //获取弧度值
            GetXYVO getXYVO = getXYS.get(i);
            String id = getXYVO.getId();
            log.info("***本次扫描发送指令***  " + odres.get(i));
            //发送十六进制的字符串并 返回十六进制的字符串
            String ret = socketUtils.sendRequest(socket, odres.get(i));
            if (StringUtils.isEmpty(ret) || ret.length() < CommonDataMeasure.RETURN_DATA) {
                log.info("读取设备数据异常");
                pointsDomain.deleteByXYP(id);
                continue;
            }
//            Thread.sleep(1000);
            int temp = 0;
            if (ret.contains(CommonDataMeasure.BAD_DATA)) {
                for (int a = 0; a < 2; a++) {
                    //延长5秒
//                    Thread.sleep(1000);
                    ret = socketUtils.sendRequest(socket, (String) odres.get(i));
                    if (ret == null || ret.equals("") || ret.length() < CommonDataMeasure.RETURN_DATA) {
                        continue;
                    }
                    if (!ret.contains(CommonDataMeasure.BAD_DATA)) {
                        break;
                    }
                    temp++;
                }
            }
//            Thread.sleep(5000);
            //解析十六进制的字符串 获取，x，y，可信度 并计算z值
            if (ret == null || ret.equals("") || ret.contains(CommonDataMeasure.BAD_DATA) || ret.length() < CommonDataMeasure.RETURN_DATA) {
                pointsDomain.deleteByXYP(id);
                continue;
            }
            GetXYVO getPoints = analysisString2(ret, sbCode, operateId, h, cfgd, getXYVO);
            if (temp >= 2 || getPoints == null) {
                temp2++;
                pointsDomain.deleteByXYP(id);
            } else {
                //根据 x y update Z 可信度 将z 可信度 值入库
                getPoints.setId(id);
                pointsDomain.updateZ(getPoints);
            }
            //临时输出
            log.info("扫描第" + i + "次" + ";命令" + odres.get(i));
        }
        System.out.println("总共扫到墙上多少次： " + temp2);
    }

    /**
     * 解析ret,返回点数据（含X,y,z<z计算出来的>，可信度）
     * h代表仓房高度减去扫描高度；
     *
     * @param ret 返回的字符串
     */
    public GetXYVO analysisString2(String ret, String sbCode, String operateId, double h, double CfHeight, GetXYVO getXYVO) {
        if (ret == null || ret.equals("")) {
            return null;
        }
        double AA, AB, AC, Aa, Ab, Ac;   //如上图,A舵机，大写ABC代表三个边，小写abc代表三个角。
        double BA, BB, BC, Ba, Bb, Bc;   //如上图,B舵机，大写ABC代表三个边，小写abc代表三个角。
        double AcTemp, BATemp;
        //获取返回的x,y,以及斜边距离z
        String[] return_str = ret.trim().split(" ", 14);
        //获取 x
        int pointX = DataMeasureUtils.hexToInt(return_str[3] + return_str[4]);
        getXYVO.setPointX(pointX);
        //获取 y
        int pointY = DataMeasureUtils.hexToInt(return_str[5] + return_str[6]);
        getXYVO.setPointY(pointY);
        //获取 hypotenuse:斜边长；
        BA = DataMeasureUtils.mmToM(DataMeasureUtils.hexToInt(return_str[7] + return_str[8] + return_str[9] + return_str[10]));
        getXYVO.setOperateId(operateId);

        //设备y值的弧度b舵机
        Bc = getXYVO.getPoint_radian(); //Bc
        //设备x,A舵机
        Ac = getXYVO.getPointx_radian(); //Ac
        //获取分区象限
        int s_region = getXYVO.getS_region();
        //仓房左边长度
        double houseLengthLeft = getXYVO.getHouseLengthLeft();
        double houseLengthRight = getXYVO.getHouseLengthRight();
        //仓房宽度
        double houseWide = getXYVO.getHouseWide();

        //判断是否打到墙上
        if (s_region == 1 || s_region == 3) {
            AB = houseLengthLeft - 0.5;
        } else {
            AB = houseLengthRight - 0.5;
        }
        AC = houseWide / 2 - 0.5;
        AcTemp = Math.atan(AB / AC) / (Math.PI / 180);  //转动方向1500+
        if (Ac > AcTemp)  //打到左墙面
        {
            log.info("222222222222222222222222222222222************************进入if打到左墙面");
            AA = AB / Math.sin(Math.toRadians(Ac));
            BB = AA;
            BATemp = BB / Math.sin(Math.toRadians(Bc));
        } else {  //打到下墙面
            log.info("222222222222222222222222222222222************************进入else打到下墙面");
            AA = AC / Math.cos(Math.toRadians(Ac));
            BB = AA;
            BATemp = BB / Math.sin(Math.toRadians(Bc));
        }
        //说明打到墙上了
        log.info("222222222222222222222222222222222****************************BA: " + BA + " BATemp:" + BATemp);
        if (BA >= BATemp) {
            log.info("*************************打到墙上了***************************BA: " + BA + " BATemp:" + BATemp);
            return null;//跳过，这个点不要了
        }
        BC = Math.cos(Math.toRadians(Bc)) * BA;//如图11，已知道Bc角度(上面已经求出并转动)，Bb角度了(90度)，以及斜边BA(上面求出)，那么就可得出BC。
        //0525
        BB = Math.sin(Math.toRadians(Bc)) * BA;
        AA = BB;
        AB = Math.sin(Math.toRadians(Ac)) * AA;
        AC = Math.cos(Math.toRadians(Ac)) * AA;

        if (s_region == 1) {
            if (((int) Bc == 0)) {
                log.info("00设备高度：" + CfHeight + "斜边长： " + h);
                getXYVO.setResult_1(houseLengthLeft);
                getXYVO.setResult_2(houseWide / 2);
                getXYVO.setPointZ((CfHeight - h));
                log.info("设备高度：" + CfHeight + "斜边长： " + BA + " y获取弧度: " + Bc + " 计算结果为：" + BC);
                log.info("仓房高度减去计算扫描点的高度最终测量结果： " + (CfHeight - h));
            } else {
                getXYVO.setResult_1(houseLengthLeft - AB);
                getXYVO.setResult_2(houseWide / 2 - AC);
                getXYVO.setPointZ((CfHeight - BC));
                log.info("设备高度：" + CfHeight + "斜边长： " + BA + " y获取弧度: " + Bc + " 计算结果为：" + BC);
                log.info("仓房高度减去计算扫描点的高度最终测量结果： " + (CfHeight - BC));
            }
        } else if (s_region == 2) {
            getXYVO.setResult_1(houseLengthLeft + AB);
            getXYVO.setResult_2(houseWide / 2 - AC);
            getXYVO.setPointZ((CfHeight - BC));
            log.info("设备高度：" + CfHeight + "斜边长： " + BA + " y获取弧度: " + Bc + " 计算结果为：" + BC);
            log.info("仓房高度减去计算扫描点的高度最终测量结果： " + (CfHeight - BC));
        } else if (s_region == 3) {
            getXYVO.setResult_1(houseLengthLeft - AB);
            getXYVO.setResult_2(houseWide / 2 + AC);
            getXYVO.setPointZ((CfHeight - BC));
            log.info("设备高度：" + CfHeight + "斜边长： " + BA + " y获取弧度: " + Bc + " 计算结果为：" + BC);
            log.info("仓房高度减去计算扫描点的高度最终测量结果： " + (CfHeight - BC));
        } else if (s_region == 4) {
            getXYVO.setResult_1(houseLengthLeft + AB);
            getXYVO.setResult_2(houseWide / 2 + AC);
            getXYVO.setPointZ((CfHeight - BC));
            log.info("设备高度：" + CfHeight + "斜边长： " + BA + " y获取弧度: " + Bc + " 计算结果为：" + BC);
            log.info("仓房高度减去计算扫描点的高度最终测量结果： " + (CfHeight - BC));
        }

        //获取可信度
        int trust = DataMeasureUtils.hexToInt(return_str[11] + return_str[12]);
        getXYVO.setPoint_trust(trust);
        //把扫描时间入库
        getXYVO.setCreateDate(new Date());
        return getXYVO;
    }

    /**
     * 获取可信度
     *
     * @param ret
     * @return
     */
    public static boolean getTrust(String ret) {
        boolean temp = false;
        String[] return_str = ret.trim().split(" ", 14);
        int trust = DataMeasureUtils.hexToInt(return_str[11] + return_str[12]);
        if (trust <= 923) {
            temp = true;
        }
        return temp;
    }

}
