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;


// 均值极差：
// 应用场景： 测点的均值极差计算
// 函数原型： MAR（测点名，时间（秒））
@Data
@NoArgsConstructor
public class AlarmFunctionMAR extends AlarmFunction {

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

    private double totalVal = 0;
    private int count = 0;

    public AlarmFunctionMAR(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);
            avgTsDataQueue.offer(tsData);
            totalVal = tsData.getDoubleValue();
            count = 1;
            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();
            totalVal -= headTsData.getDoubleValue();
            count--;
        }

        tsDataQueue.offer(tsData);
        lastUpdateTsData = tsData;
        totalVal += tsData.getDoubleValue();
        count++;

        long avgTs = new Date().getTime();
        double avgVal = totalVal / count;
        DoubleTsData avgTsData = new DoubleTsData(avgTs, (short)0, avgVal);
        avgTsDataQueue.offer(avgTsData);

        DoubleTsData headTsData2 = avgTsDataQueue.peek();
        while (avgTs - headTsData2.getTs() > interval * 1000 &&
                avgTsDataQueue.size() > 0) {
            headTsData2 = avgTsDataQueue.poll();
        }

        DoubleTsData minTsData = avgTsData;
        DoubleTsData maxTsData = avgTsData;

        for (int i = 0; i < avgTsDataQueue.size(); i++) {
            if (minTsData.getDoubleValue() >= avgTsDataQueue.get(i).getDoubleValue()) {
                minTsData = avgTsDataQueue.get(i);
            }
            if (maxTsData.getDoubleValue() <=  avgTsDataQueue.get(i).getDoubleValue()) {
                maxTsData = avgTsDataQueue.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());
    }
}
