package jtznyj.engineer.service.dataDiagnosticService;

import cn.hutool.core.date.DateUtil;
import lombok.Getter;
import jtznyj.engineer.service.dataDiagnosticService.data.AlarmPoint;
import jtznyj.engineer.service.dataDiagnosticService.data.DrillState;
import jtznyj.engineer.service.winform.data.MonitorContext;
import lombok.extern.slf4j.Slf4j;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;

/**
 * 单井趋势--振幅分析诊断
 */
@Slf4j
public class WellRangeTrendDataDiagnostic extends WellTrendDataDiagnostic {
    protected class PointPair {
        public double _x;

        public double _y;

        public PointPair(double x, double y) {
            this._x = x;
            this._y = y;
        }
    }

    @Getter
    protected class TimeAnglePair {
        private double angle;

        private double sec;

        private double value;

        private Date date;

        private Hashtable data;

        public TimeAnglePair() {
        }

        public TimeAnglePair(double angle, double sec, double value, Date dtCurrent, Hashtable data) {
            this.angle = angle;
            this.sec = sec;
            this.date = dtCurrent;
            this.value = value;
            this.data = data;
        }
    }

    public static double CalcAngle(PointPair mp0, PointPair mpAngle, PointPair mp1) {
        double xV = mpAngle._x - mp0._x;
        double yV = mpAngle._y - mp0._y;
        double xV2 = mp1._x - mpAngle._x;
        double yV2 = mp1._y - mpAngle._y;
        double result;
        if ((0.0 == xV && 0.0 == yV) || (0.0 == xV2 && 0.0 == yV2)) {
            result = 0.0;
        } else {
            result = Math.acos((xV * xV2 + yV * yV2) / Math.sqrt((xV * xV + yV * yV) * (xV2 * xV2 + yV2 * yV2))) * 180.0 / 3.1415926535897931;
        }
        return result;
    }

    /**
     * 趋势数据
     */
    private List<TimeAnglePair> _listMonitorRangeAngles = new ArrayList<>();

    @Override
    protected void tryAddTimeValue(Hashtable record) {
        if (record != null) {
            //确保监控数据都在监控时间范围之内
            Date dt = (Date) record.get(MonitorContext.fieldTime);
            if (this.param._monitorTime > 0) {
                //处理超出监控时间区间的数据
                if (this.trendDataList.size() > 0) {
                    //时差
                    int totalSeconds = Math.abs((int) ((dt.getTime() - trendDataList.get(trendDataList.size() - 1).getDateTime().getTime()) / 1000));
                    if (totalSeconds > this.param._monitorTime) {
                        //超过了监控时间范围
                        this.trendDataList.remove(trendDataList.size() - 1);
                    }
                }

                if (this._listMonitorRangeAngles.size() > 0) {
                    //时差
                    int totalSeconds = Math.abs((int) ((dt.getTime() - _listMonitorRangeAngles.get(_listMonitorRangeAngles.size() - 1).getDate().getTime()) / 1000));
                    if (totalSeconds > this.param._monitorTime) {
                        //超过了监控时间范围
                        this._listMonitorRangeAngles.remove(_listMonitorRangeAngles.size() - 1);
                    }
                }
            }
            double v = (double) record.get(this.param._originalField);
            if (v != DOUBLE_NULL_VALUE) {
                TimeValuePair tv = new TimeValuePair(dt, v, record);
                this.trendDataList.add(0, tv);

                //角度数据处理
                if (this.trendDataList.size() > 2) {
                    int lastIndex = this.trendDataList.size() - 1;
                    TimeValuePair<Hashtable> back3 = this.trendDataList.get(lastIndex - 2);
                    PointPair mp0 = new PointPair(0.0, back3.getValue());
                    TimeValuePair<Hashtable> back2 = this.trendDataList.get(lastIndex - 1);
                    PointPair mpAngle = new PointPair((back2.getDateTime().getTime() - back3.getDateTime().getTime()) / 1000,
                            back2.getValue());
                    TimeValuePair<Hashtable> back1 = this.trendDataList.get(lastIndex);
                    PointPair mp = new PointPair((back1.getDateTime().getTime() - back2.getDateTime().getTime()) / 1000,
                            back1.getValue());
                    double angle = CalcAngle(mp0, mpAngle, mp);
                    TimeAnglePair ma = new TimeAnglePair(angle, Math.abs(mpAngle._x), mpAngle._y, back2.getDateTime(), back1.getData());
                    this._listMonitorRangeAngles.add(0, ma);
                }
            }
        }
    }

    /**
     * 单一模型诊断分析
     *
     * @param record
     * @param currentPumpState
     * @param drillState
     * @param lastPumpRunTime
     * @param lastPumpStopTime
     * @return
     */
    @Override
    protected DataDiagnosticAnalyzeResult internalAnalyse(Hashtable record, boolean currentPumpState,
                                                          DrillState drillState, Date lastPumpRunTime, Date lastPumpStopTime) {
        //前置条件检查
        DataDiagnosticAnalyzeResult analyseResult = this.beforeAnalyse(record, currentPumpState, drillState, lastPumpRunTime, lastPumpStopTime);
        if (!analyseResult.result) {
            //提前退出--不报警
            analyseResult.result = false;
            return analyseResult;
        }

        analyseResult.result = false;
        int iCount = this.trendDataList.size();

        //================================================
        //振幅分析--begin
        int angleCount = 0;
        double dAllSec = 0.0;
        TimeAnglePair ma;
        for (int j = 0; j < this._listMonitorRangeAngles.size(); j++) {
            ma = this._listMonitorRangeAngles.get(j);
            //45~135
            //30~150
            //40~140
            if (ma.getAngle() > 40.0 && ma.getAngle() < 140.0) {
                angleCount++;
                dAllSec += ma.getSec();
                //++++++++
                //只要在监控时间区间，预警持续时间超过规定的阈值即可报警
                if (angleCount > this.param._zdCount && dAllSec > this.param._continuedTime) {
                    break;
                }
            } else {
                angleCount = 0;
                dAllSec = 0;
            }
        }
        this._secondState.SetRange(this.trendDataList.get(0).getDateTime(), this.trendDataList.get(iCount - 1).getDateTime(), angleCount);
//        System.out.println(angleCount);
//        System.out.println(this.param._zdCount);
//        System.out.println(dAllSec);
//        System.out.println(this.param._continuedTime);

        //振幅分析关键的判断条件，得出结论
        if (this._listMonitorRangeAngles.size() > 0 && angleCount > this.param._zdCount && dAllSec > this.param._continuedTime) {
            AlarmPoint description = new AlarmPoint();
            ma = this._listMonitorRangeAngles.get(_listMonitorRangeAngles.size() - 1);
            description.name = this.param._name;
            description.field = this.param._realField;
            description.alarmType = this.param._analyseType.toString();
            //description._dtStart = this._listMonitorRangeAngles.get(0).getDate();
            description.alarmDate = new Date(ma.getDate().getTime());
            //description._startValue = this._listMonitorRangeAngles.get(0).getValue();
            description.value = ma.getValue();

            //description._startDepth = getWellDepth(this._listMonitorRangeAngles.get(0).getData());//getWellDepth(this.trendDataList.get(0).getData())
            // description._startZtDepth = getDrillDepth(this._listMonitorRangeAngles.get(0).getData());//getDrillDepth(this.trendDataList.get(0).getData())
            description.depth = getWellDepth(ma.getData());//getWellDepth(this.trendDataList.get(iCount-1).getData())
            description.ztDepth = getDrillDepth(ma.getData());//getDrillDepth(this.trendDataList.get(iCount-1).getData())
            this._secondState.SetAlarm(true);
            analyseResult.result = true;
            analyseResult.description = description;

            description.des = this.setDes("", description, angleCount);
            description.WarningCause = "异常原因: " + DataDiagnosticEngineer.getRealFieldName(description.field)
                    + " 超出振幅设置(大于" + this.param._zdCount + "次),原始值:" + ma.getValue() + this.param._unit;
            //description._secondState = (SecondState) this._secondState.Clone();

            log.debug(" {}, 数据趋势rangeTrend,{}",
                    DataDiagnosticEngineer.packStackStr(Thread.currentThread().getStackTrace()),
                    description.WarningCause);
        }
        //振幅分析--end
        //================================================

        return analyseResult;
    }

    private String setDes(String jh, AlarmPoint description, int angleCount) {

        DecimalFormat decimalFormat = new DecimalFormat("###################.###########");
        DecimalFormat valueDf = new DecimalFormat("0.00");

        String des = "";
        des += "[" + this.jh + " " + description.name + "]";
        des += "\n";
        des += "时间: " + DateUtil.format(description.alarmDate, "yyyy-MM-dd HH:mm:ss");
        des += "\n";
        des += "深度段: " + description.depth;
        des += "\n";
        des += "异常原因: " + DataDiagnosticEngineer.getRealFieldName(description.field)
                + " 超出振幅设置(大于" + this.param._zdCount + "次),当前次数" + angleCount
                + "次" + ",原始值:" + description.value + this.param._unit;
        return des;
    }


}
