package com.gyee.alarm.rule;


import com.gyee.alarm.model.vo.DeviceTypeValue;
import com.gyee.alarm.rule.expression.AlarmExpression;
import com.gyee.alarm.util.realtimesource.timeseries.DoubleTsData;
import com.gyee.alarm.util.realtimesource.timeseries.TsDataType;
import com.gyee.common.model.PointData;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;
import java.util.LinkedList;


// 移动极差：移动极差（Moving Range）是指两个或多个连续样本值中最大值与最小值之差。
// 这种差是按这样方式计算的：每当得到一个额外的数据点时，就在样本中加上这个新的点，
// 同时删除其中时间上“最老的”点，然后计算与这点有关的极差，因此每个极差的计算至少
// 与前一个极差的计算共用一个点的值。
// 一般说来，移动极差用于单值控制图，并且通常用两点（连续的点）来计算移动极差。
// 应用场景： 测点的移动极差超限报警
// 函数原型： MR（测点名，时间（秒））
@Data
@NoArgsConstructor
public class AlarmFunctionMR extends AlarmFunction {

    private DoubleTsData lastUpdateTsData;
    //private DoubleTsData minTsData;
    //数据缓存
    private LinkedList<DoubleTsData> tsDataQueue = new LinkedList<DoubleTsData>();
    private long interval; //时间范围，单位秒
    //private double threshold; //阈值

    public AlarmFunctionMR(AlarmExpression alarmExpression, DeviceTypeValue tType, String tId) {
        super(alarmExpression, tType, tId);
        String exp = alarmExpression.getExpression();
        exp = exp.substring(3, exp.length() - 1);
        String[] arr = exp.split(",");
        interval = Long.valueOf(arr[1]);
        //threshold = Double.valueOf(arr[2]);
    }

    @Override
    public Object explain() {
        if (this.getAlarmExpression().getVarList() == null || this.getAlarmExpression().getVarList().size() < 1)
            return false;
        String varName = this.getAlarmExpression().getVarList().get(0);
        DoubleTsData tsData = getValue(varName);
        if (tsData == null)
            return 0;
        if (lastUpdateTsData == null) {
            lastUpdateTsData = tsData;
            tsDataQueue.offer(tsData);
            return 0;
        }

        //如果数据时间戳在变化，最新数据时间作为基准时间
        //否则，认为数据已不更新，取系统时间和最新数据时间较大者为基准时间
        long ts = new Date().getTime();
        if (tsData.getTs() > lastUpdateTsData.getTs())
            ts = tsData.getTs();
        else if (tsData.getTs() > ts)
            ts = tsData.getTs();


        DoubleTsData headTsData = tsDataQueue.peek();

        while (ts - headTsData.getTs() > interval * 1000 &&
                tsDataQueue.size() > 0) {
            headTsData = tsDataQueue.poll();
        }
        tsDataQueue.offer(tsData);
        lastUpdateTsData = tsData;
        DoubleTsData minTsData = tsData;
        DoubleTsData maxTsData = tsData;

        for (int i = 0; i < tsDataQueue.size(); i++) {
            if (minTsData.getDoubleValue() >= tsDataQueue.get(i).getDoubleValue()) {
                minTsData = tsDataQueue.get(i);
            }
            if (maxTsData.getDoubleValue() <=  tsDataQueue.get(i).getDoubleValue()) {
                maxTsData = tsDataQueue.get(i);
            }
        }
        return maxTsData.getDoubleValue() - minTsData.getDoubleValue();
    }


    public DoubleTsData getValue(String varName) {
        PointData tagInfo = this.getTagInfo(varName);

        return new DoubleTsData(tagInfo.getPointTime(), (short) 0, tagInfo.getPointValueInDouble());
    }
}
