<script setup>
import { ref, watch, onMounted, watchEffect } from "vue";
import * as echarts from 'echarts';
import { FixServiceClient }  from "@/serviceClients/thk/FixServiceClient";

//#region 图表控制

var YMax = ref(NaN);
var YMin = ref(NaN);
var XMax = ref(NaN);
var XMin = ref(NaN);
/** 显示在chart上的数据 */
var Values = [];
//#endregion

//#region 界面统计值
        
/** 平均值 */
const Average=ref(NaN);

/** 最大值 */
const Max=ref(NaN);

/** 最小值 */
const Min=ref(NaN);
/** 2sigma */
const Sigma2=ref(NaN);
//#endregion

//#region 状态
/** AD值模式时,Y轴范围 */
const YRangeAd = ref(1000);
/** Value模式时,Y轴范围 */
const YRange = ref(10);

/** 数据长度 */
const Len = ref(2000);
/** 测试时间 */
const Time = ref('');
/** 间隔,单位ms */
const Intervalms = ref(1);

/** 混合数 */
const Mix = ref(1);

/** 暂停 */
const IsPaused = ref(false);

/**  AD值模式 */
const IsADMode = ref(false);
//#endregion

/** 真实接收的数据，与 Values 不一样， Values只是mDatas的一部分 */
var mDatas = [];

/** 自动Y轴中值时，Y轴中值的Value */
const AutoTarget = ref(0);

/** 多少个 mDatas 显示为一个 Values， 限定屏幕最大显示2000个数据 */
var values_scale = 1;

/** 限定屏幕最大显示2000个数据 */
const values_cap = 1000;



//#region 用于间隔

/** 显示数据用，mDatas->mPoints 的转换 */
var data_show = [];

/** interval 时间内数据整合 */
var data_interval = [];

/** data_interval 的总时间 */
var data_interval_totaltime = 0;

/** 数据混合 */
var data_mix = [];
//#endregion

var myChart;


const fixService = new FixServiceClient();
fixService.Init(`api/Fix`,"localhost:5540");

onMounted(() => {
  console.log('onMounted');
  var chartDom = document.getElementById('main');
  myChart = echarts.init(chartDom);

  var option = {
  xAxis: {
    type: 'value',
    splitLine: {
      show: true,
      lineStyle: {
        width: 1,
        type: 'dashed'
      }
    },
    // max:XMax.value,
    // min:XMin.value,
    max:'dataMax',
    min:'dataMin'
  },
  yAxis: {
    type: 'value',
    splitLine: {
      show: true,
      lineStyle: {
        width: 1,
        type: 'dashed'
      }
    },
    // max:YMax.value,
    // min:YMin.value,
    max:'dataMax',
    min:'dataMin',
    
  },
  series: [
    {
      name: '1ms data',
      type: 'line',
      showSymbol: false
    }
  ],
  animation:false
  };
  
  myChart.setOption(option);

  updateIsADMode();
  updateX();
  updateY();

  unittest();
});

function unittest(){
  Values = [];

  var values = [];
  for (var i = 0; i < values_cap; i++)
  {
      var v = (Math.sin(i * Math.PI / 400.0) * 0.6) * 2 + 72 + (Math.random() - 0.5) * 1;
      values.push(v);
  }
  Values = Values.concat(values);
  updateSeriesIntoChart();
}

watch(()=>fixService.Data.UpdateTime,async ()=>{
  if(IsPaused.value)
    return;
  console.log("watch UpdateTime");
  var response = await fixService.GetFixDatas();
  fixService_GetFixDatas(response);
});

watch(AutoTarget,()=> updateAutoTarget());
watch(Len, ()=>updateX());
watch([YRange,YRangeAd], ()=>updateY());
watch(IsADMode,()=>{
  updateIsADMode();
  updateY();
});

watch([XMax,XMin],()=>updateXIntoChart());
watch([YMax,YMin],()=>updateYIntoChart());


function updateAutoTarget(){

  var yrange = IsADMode.value ? YRangeAd.value : YRange.value;
  if ((!isNaN(YMax.value)) && (!isNaN(YMin.value))){
    //Y轴最大，最小同时有效

    //中值
    var mid = (YMax.value + YMin.value) / 2;
    //中间范围
    var range = (YMax.value - YMin.value) / 2;
    
    //中间范围最大最小值
    var ymax2 = mid + range / 2;
    var ymin2 = mid - range / 2;

    if ((AutoTarget.value < ymax2) && (AutoTarget.value > ymin2)){
      //均值在中间范围内
      
      if (Math.abs(range - yrange) < 0.05){
        //还在范围内，不需要改变 Y轴范围
        return;
      }
    }
  }
  if(!isNaN(AutoTarget.value)){
    YMax.value = AutoTarget.value + yrange;
    YMin.value = AutoTarget.value - yrange;
  }
  else{
    YMax.value = NaN;
    YMin.value = NaN;
  }
}
function updateX(){
  console.log('updateX');
    if (Len.value > values_cap)
        values_scale = Len.value / values_cap;
    else
        values_scale = 1;

    XMax.value = Math.floor(Len.value / values_scale);//最后算出来 的数量肯定大于 values_cap,没关系，反正数据量少了
    XMin.value = 0;
}
function updateY(){
  console.log('updateY');
  updateAutoTarget();
}
function updateIsADMode(){
    //清空全部数据
    mDatas = [];
    data_interval = [];
    data_mix = [];
    data_show = [];
    data_interval_totaltime = 0;
    //需要刷新界面
    Values = [];
    updateSeriesIntoChart();
    updateStat();
}
//#region COMMON
function getAverage(arr) {
  var sum = arr.reduce((acc, val) => acc + val, 0);
  var cnt = arr.length;
  return sum/cnt;
}
function getSigma(arr){
  if(arr.length<2)
    return NaN;
  var avg = getAverage(arr);
  var sum_pow = arr.reduce((acc, val) => acc + Math.pow(val-avg,2), 0);
  return Math.sqrt(sum_pow/(arr.length-1));
}
function onlyNumber(num){
  let n = String(num);
  const t = n.charAt(0);
  // 先把非数字的都替换掉，除了数字和.
  n = n.replace(/[^\d\\.]/g, '');
  // 必须保证第一个为数字而不是.
  n = n.replace(/^\./g, '');
  // 必须保证第一个数字非0
  n = n.replace(/^0(\d)/g, ($0, $1) => $1);
  // 保证只有出现一个.而没有多个.
  n = n.replace(/\.{2,}/g, '.');
  // 保证.只出现一次，而不能出现两次以上
  n = n.replace('.', '$#$').replace(/\./g, '').replace(
    '$#$', '.');
  // 如果第一位是负号，则允许添加
  if (t === '-') {
    n = '-' + n;
  }
  return n;
}
function onlyInputNumber (event) {
       if (event.target.value === '') return
        event.target.value = onlyNumber(event.target.value)
        if (this.max !== '' && !isNaN(Number(this.max)) && (+event.target.value > +this.max)) {
          event.target.value = this.max
        }
        if (this.min !== '' && !isNaN(Number(this.min)) && (+event.target.value < +this.min)) {
          event.target.value = this.min
        }
        this.datanumber = event.target.value
    }
function onlyNumberBlur (event) {
    if (isNaN(Number(event.target.value))) {
      event.target.value = ''
      this.datanumber = event.target.value
    }
}

//#endregion



function fixService_GetFixDatas(response){
  //可以为null，需要判断
  if(response == null)
    return;

  //输入1ms 的数据， 输出 混合为mix，间隔为 interval 的数据
  var data_out = [];
  for (var i = 0; i < response.thicks.length; i++){

      var v = IsADMode.value ? response.ADs[i] : response.thicks[i];
      data_interval.push(v);
      data_interval_totaltime += 1;//response.ts;

      if (data_interval_totaltime >= Intervalms.value){
        //计算均值
        var avg = getAverage(data_interval);
        data_interval=[];
        do{
          data_interval_totaltime -= Intervalms.value;
          data_mix.push(avg);
        } while (data_interval_totaltime >= Intervalms.value);//当 reponse.ts = 1.28， Interval=1时，需要添加更多的数据

        var len = data_mix.length - Mix.value;
        if (len > 0)
          data_mix.splice(0,len);
          avg = getAverage(data_mix);
          data_out.push(avg);
      }
  }
  if (data_out.length > 0){
    Time.value = response.time;
    addPoint(data_out);
  }      
}

/** 1ms 经过重新 划分间隔， 混合后的数据，添加到 mDatas
 * @param {double[]} datas 
 */
function addPoint(datas){
    mDatas = mDatas.concat(datas);

    //删除数据
    if (mDatas.length  > Len.value)
        mDatas.splice(0, mDatas.length - Len.value);

    //更新状态
    updateStat();

    //增量数据，添加到 Values
    refreshValuesMore(datas);
}
function updateStat(){
    if (mDatas.length == 0)
    {
        Max.value = NaN;
        Min.value = NaN;
        Average.value = NaN;
        Sigma2.value = NaN;
        AutoTarget.value = NaN;
        return;
    }
    Max.value = Math.round(Math.max(...mDatas)*10)/10;
    Min.value = Math.round(Math.min(...mDatas)*10)/10;
    Average.value = Math.round(getAverage(mDatas)*10)/10;
    Sigma2.value = Math.round(getSigma(mDatas)*10 * 2)/10;

    if (mDatas.length > 100)
    {
        //最后100个数据的平均值
        
        AutoTarget.value = Math.round( getAverage(mDatas.slice(-100))*10)/10;
    }
    else
    {
        AutoTarget.value = Average.value;
    }
}

/** 增量数据，添加到 Values */
function refreshValuesMore(datas){
    
  
  //显示在屏幕的数据，只能有2000个
    if (values_scale <= 1)
    {
      Values = Values.concat(datas);
      var remove_cnt = Values.length- Len.value;
      if(remove_cnt>0){
        Values.splice(0,remove_cnt);
      }
    }
    else
    {
      for(var i=0;i<datas.length;i++){
        data_show.push(datas[i]);
        if (data_show.length >= values_scale){
          var avg = getAverage(data_show);
            Values.push(avg);
            data_show=[];
        }

        //删除数据
        var remove_cnt = Values.length- Math.floor(Len.value / values_scale);
        if(remove_cnt>0){
          Values.splice(0,remove_cnt);
        }
      }
    }
    //刷新显示
    updateSeriesIntoChart();
}

/** 把Value绑定到Series */
function updateSeriesIntoChart(){
  //附加X轴
  var Values2 = [];
  for(var i=0;i<Values.length;i++){
    Values2.push([i,Values[i]]);
  }
  console.log('updateSeriesIntoChart');
  myChart.setOption({
    series: [
      {
        data: Values2
      },
    ],
  });
}
function updateXIntoChart(){

  var xAxis = {
    type: 'value',
    splitLine: {
      show: true,
      lineStyle: {
        width: 1,
        type: 'dashed'
      }
    }
  };
  if(!isNaN(XMax.value))
    xAxis.max = XMax.value;

    if(!isNaN(XMin.value))
    xAxis.min = XMin.value;
    console.log(`updateXIntoChart ${xAxis}`);

  myChart.setOption({
    xAxis
  });
}
function updateYIntoChart(){
  
  var yAxis = {
    type: 'value',
    splitLine: {
      show: true,
      lineStyle: {
        width: 1,
        type: 'dashed'
      }
    }
  };
  if(!isNaN(YMax.value))
    yAxis.max = YMax.value;

  if(!isNaN(YMin.value))
    yAxis.min = YMin.value;
    console.log(`updateYIntoChart ${JSON.stringify(yAxis)}`);
    myChart.setOption({
    yAxis:yAxis
  });
}

function play(){
  IsPaused.value = false;
}
function stop(){
  IsPaused.value = true;
}
function changeADMode(){
  IsADMode.value = !IsADMode.value;
  updateIsADMode();
  updateY();
}
function clear(){
  updateIsADMode();
}
function disconnect(){
  fixService.Dispose();
}
function connect(){
  fixService.ReConnect();
}
</script>

<template>
  <div >
    <h1>Test Fix</h1>
    <div style="display: flex; flex-direction: column;">
      <div style="display: flex; flex-direction: row; margin: 3px;" v-for="(value, key) in fixService.Data">
        <label style="width: 150px;">{{key}}</label>
        <span style="margin-left: 20px; padding-left: 5px; padding-right: 5px;" >{{ fixService.Data[key] }}</span>
      </div>
      <div id="main" style="width: 600px; height: 400px"></div>
      <p>{{ Time }}</p>
      <p>Max: {{ Max }}  ;Min: {{ Min }}  ;Average: {{ Average }}; 2σ: {{ Sigma2 }}; <br/>AutoTarget: {{ AutoTarget }};</p>
      <div style="display: flex; flex-direction: row">
      <button
        style="margin: 3px; padding: 3px 10px"
        :disabled="!IsPaused"
        @click="play()"
      >
        START
      </button>
      <button
        style="margin: 3px; padding: 3px 10px"
        :disabled="IsPaused"
        @click="stop()"
      >
        STOP
      </button>
      <button
        style="margin: 3px; padding: 3px 10px"
        @click="changeADMode()"
      >
        {{IsADMode?'AD':'VALUE'}}
      </button>
      <button style="margin: 3px; padding: 3px 10px" @click="clear()">CLEAR</button>
      <button style="margin: 3px; padding: 3px 10px" @click="disconnect()">DISCONNNECT</button>
      <button style="margin: 3px; padding: 3px 10px" @click="connect()">CONNECT</button>
    </div>
    <div style="display: flex; flex-direction: column;">
      <div style="display: flex; flex-direction: row;">
        <label>YRangeAd</label>
        <input type="number" v-model.number="YRangeAd" :min="100" @input="onlyInputNumber" @blur="onlyNumberBlur"/>
      </div>
      <div style="display: flex; flex-direction: row;">
        <label>YRange:{{ YRange }}</label>
        <input type="number" v-model.number.lazy="YRange" />
        
      </div>
      <div style="display: flex; flex-direction: row;">
        <label>Len</label>
        <input type="text" v-model.number="Len" :min="100" @input="onlyInputNumber" @blur="onlyNumberBlur"/>
      </div>

      <div style="display: flex; flex-direction: row;">
        <label>Mix</label>
        <input type="text" v-model.number="Mix" :min="1" @input="onlyInputNumber" @blur="onlyNumberBlur"/>
      </div>
      <div style="display: flex; flex-direction: row;">
        <label>Intervalms</label>
        <input type="text" v-model.number="Intervalms" :min="1" @input="onlyInputNumber" @blur="onlyNumberBlur"/>
      </div>
    </div>
    </div>
  </div>
</template>

<style scoped>

</style>
