//  Copyright (c) 2024 Huawei Technologies Co., Ltd.
//  openUBMC is licensed under Mulan PSL v2.
//  You can use this software according to the terms and conditions of the Mulan PSL v2.
//  You may obtain a copy of Mulan PSL v2 at:
//        #  http://license.coscl.org.cn/MulanPSL2
//  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
//  EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
//  MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
//  See the Mulan PSL v2 for more details.
import { ISensorData, ISensorOptions, IFloatXY, INodeData } from '../model/sensor.datatype';
import { getErrorTranslate, traduction } from '@/utils/language';
interface Data {
  [prop: number]: number;
}
// 反距离加权P w_i = 1/d_i^p
const p = 2;
const innerUnit = 1;
const reciprocalUnit = Math.floor(1 / innerUnit);
const reciprocalUnitSq = Math.pow(reciprocalUnit, 2);

const echartsGrid3D = {
  viewControl: {
    rotateSensitivity: 0,
    zoomSensitivity: 0,
    alpha: 25,
    beta: 40,
  },
  boxHeight: 20,
  boxDepth: 160,
  boxWidth: 100,
  show: false,
  light: {
    main: {
      alpha: 90,
      intensity: 0,
    },
    ambient: {
      intensity: 1.2,
    },
  },
};
const echartsBackGrid3D = {
  viewControl: {
    rotateSensitivity: 0,
    zoomSensitivity: 0,
    alpha: 25,
    beta: 221,
  },
  boxHeight: 20,
  boxDepth: 160,
  boxWidth: 100,
  top: 20,
  left: -50,
  show: false,
  light: {
    main: {
      alpha: 100,
      beta: 100,
      intensity: 0,
    },
    ambient: {
      intensity: 1.2,
    },
  },
};

export function getSensorEchartOptions(data: ISensorData, template: any): object {
  const { data: echartData, max, min } = renderEchartOptionData(data);
  const scatter3DData = renderScatterData(data);
  const option = {
    tooltip: {
      confine: true,
      enterable: true,
      backgroundColor: 'var(--o-bg-color-light)',
      formatter: (params: any): string => {
        return getFormatter(params, template);
      },
      position: 'top',
      borderColor: 'var(--o-bg-color-light)',
    },
    label: {
      show: false,
    },
    pointerSize: 100,
    useCoarsePointer: true,
    visualMap: {
      show: false,
      dimension: 2,
      min: Math.min(min || 0, 0),
      max: Math.max(max || 0, 120),
      inRange: {
        color: ['#5cffa2', '#deff20', '#ffaa00', '#ff5e00', '#ff0800'],
      },
    },
    xAxis3D: {
      type: 'value',
    },
    yAxis3D: {
      type: 'value',
    },
    zAxis3D: {
      type: 'value',
    },
    grid3D: echartsGrid3D,
    series: getSeries(scatter3DData, echartData),
  };
  return option;
}
export function getSensorEchartOptionsBack(data: ISensorData, template: any): object {
  const { data: echartData, max, min } = renderEchartOptionData(data);
  const scatter3DData = renderScatterData(data);
  const option = {
    tooltip: {
      confine: true,
      enterable: true,
      backgroundColor: 'var(--o-bg-color-light)',
      formatter: (params: any): string => {
        return getFormatter(params, template);
      },
      position: 'top',
      borderColor: 'var(--o-bg-color-light)',
    },
    pointerSize: 100,
    useCoarsePointer: true,
    visualMap: {
      show: false,
      dimension: 2,
      min: Math.min(min || 0, 0),
      max: Math.max(max || 0, 120),
      inRange: {
        color: ['#5cffa2', '#deff20', '#ffaa00', '#ff5e00', '#ff0800'],
      },
    },
    xAxis3D: {
      type: 'value',
    },
    yAxis3D: {
      type: 'value',
    },
    zAxis3D: {
      type: 'value',
    },
    grid3D: echartsBackGrid3D,
    series: getSeries(scatter3DData, echartData),
  };
  return option;
}
// 拆分echarts配置项
function getSeries(scatter3DData: any, echartData: any) {
  return [
    {
      type: 'scatter3D',
      symbol: 'emptyCircle',
      data: scatter3DData,
      label: {
        show: false,
        position: 'top',
      },
      symbolSize: 8,
      itemStyle: {
        borderWidth: '1',
        borderColor: '#fff',
        opacity: 0,
      },
    },
    {
      type: 'surface',
      wireframe: {
        lineStyle: {
          color: '#C9D8E9',
          opacity: 0.2,
        },
      },
      data: echartData,
      tooltip: {
        show: false,
      },
    },
  ];
}
function renderEchartOptionData(data: ISensorData): ISensorOptions {
  const width = data.XScope;
  const height = data.YScope;
  const originData = {};
  data.Sensors.filter((v: any) => v.Status !== '').forEach((sensor:any) => {
    const x = sensor.CoordinateX;
    const y = sensor.CoordinateY;
    if (x < 0 || x >= width) {
      return;
    }
    if (y < 0 || y >= height) {
      return;
    }

    const position = x + (y * width);
    if (originData[position]) {
      originData[position][0] = Math.max(originData[position][0], sensor.ReadingValue);
      originData[position][1] = Math.max(originData[position][1], sensor.UpperThreshold[0] || 0);
      originData[position][2] = Math.max(originData[position][2], sensor.UpperThreshold[1] || 0);
      originData[position][3] = sensor.Name;
      originData[position][4] = sensor.Status;
    } else {
      originData[position] = [];
      // 传感器读值
      originData[position][0] = sensor.ReadingValue;
      // 轻微上门限阈值
      originData[position][1] = sensor.UpperThreshold[0];
      // 严重上门限阈值
      originData[position][2] = sensor.UpperThreshold[1];
      // 传感器名
      originData[position][3] = sensor.Name;
      // 传感器状态
      originData[position][4] = sensor.Status;
    }
  });
  const step = 5;
  const degree = 1.5;
  const result = interpolation(originData, width, height, step, degree);
  const surface = float32Array2Data(result, width, height);

  return {
    data: surface,
    max: Math.max(...result.value, 120),
    min: Math.min(...result.value, 0),
    step,
    degree,
  };
}
function renderScatterData(data: any): object {
  // 场景描述：此处添加让散点图z轴高于3d曲面图需将其值增大，与实际显示数据无关
  const zValue = 2;
  return data.Sensors.filter((v: any) => v.ReadingValue !== null).map((e: any) => {
    return [
      e.CoordinateX + 1,
      e.CoordinateY + 1,
      e.ReadingValue < 0 ? 0 + zValue : e.ReadingValue + zValue,
      e.UpperThreshold[0],
      e.UpperThreshold[1],
      e.Name,
      e.Status,
      e.ReadingValue,
      e.UpperThreshold[2],
    ];
  });
}
function getFormatter(params: any, template: any): string {
  const major = traduction('ALARM_EVENT_MAJOR');
  const warm = traduction('ALARM_EVENT_MINOR');
  const threshold = traduction('MONITOR_THRESHOLD');
  const normal = traduction('LICENSE_STATUS_NORMAL');
  const critical = traduction('ALARM_EVENT_CRITICAL');
  const minor = traduction('ALARM_EVENT_MINOR');
  const fatal = traduction('ALARM_EVENT_CRITICAL');
  let dotHtml1 = '';
  let dotHtml2 = '';
  let text = '';
  if (params.data[5]) {
    dotHtml1 = getToolTip(params)[0];
    dotHtml2 = getToolTip(params)[1];
  }
  let imgHtml = '';
  if (params.data[6]) {
    imgHtml = getImgDom(params, normal, minor, major, critical);
  }
  if (template.value === '℃') {
    text = `${params.data[7].toFixed(0)}${template.value}</span></div>
    <div class="echarts-gl-formatter-block-item3"><div class="echarts-gl-formatter-block-label3">${threshold}</div><div class="echarts-gl-formatter-sub-item"><span>${warm}:</span><span>${
  params.data[3] ? params.data[3].toFixed(0) : 'N/A'}${
  params.data[3] ? template.value : ''}</span><span class="echarts-gl-formatter-sub-label">${major}:</span><span>${
  params.data[4] ? params.data[4] : 'N/A'}${params.data[4] ? template.value : ''}</span>
    <span class="echarts-gl-formatter-sub-label">${fatal}:</span><span>${params.data[8] ? params.data[8] : 'N/A'}${
  params.data[8] ? template.value : ''}</span></div></div>
    </div>
    <div class="echarts-gl-formatter-pointer"></div>
    </div>`;
  } else {
    text = `${(((params.data[7] * 9) / 5) + 32).toFixed(0)}${template.value}</span></div>
    <div class="echarts-gl-formatter-block-item3"><div class="echarts-gl-formatter-block-label">${threshold}</div><div class="echarts-gl-formatter-sub-item"><span>${warm}:</span><span>${
  params.data[3] ? (((params.data[3] * 9) / 5) + 32).toFixed(0) : 'N/A'}${
  params.data[3] ? template.value : ''};</span><span class="echarts-gl-formatter-sub-label">${major}:</span><span>${
  params.data[4] ? (((params.data[4] * 9) / 5) + 32).toFixed(0) : 'N/A'}${params.data[4] ? template.value : ''}</span>
    <span class="echarts-gl-formatter-sub-label">${fatal}:</span><span>${
  params.data[8] ? (((params.data[8] * 9) / 5) + 32).toFixed(0) : 'N/A'}${params.data[8] ? template.value : ''}</span></div></div>
    </div>
    <div class="echarts-gl-formatter-pointer"></div>
    </div>`;
  }
  return dotHtml1 + imgHtml + dotHtml2 + text;
}
function getToolTip(params: any): string[] {
  const temperature = traduction('FDM_TEMPERATURE');
  const status = traduction('STATUS');
  let dotHtml1 = '';
  let dotHtml2 = '';
  dotHtml1 = `<div class="echarts-gl-formatter-other-row-value-box">
    <div class="echarts-gl-formatter-title">${params.data[5]}</div>
    <div class="echarts-gl-formatter-line"></div>
    <div class="echarts-gl-formatter-block">
    <div class="echarts-gl-formatter-block-item1">
      <span class="echarts-gl-formatter-block-label">${status}</span>
      <span class="echarts-gl-formatter-block-content">`;
  dotHtml2 = `</span></div>
    <div class="echarts-gl-formatter-block-item2"><span class="echarts-gl-formatter-block-label2">${temperature}</span><span>`;
  return [dotHtml1, dotHtml2];
}
function getImgDom(params: any, normal: any, minor: any, major: any, critical: any): string {
  let imgHtml = '';
  if (params.data[6] === 'ok') {
    imgHtml = `
    <el-tag class="echarts-gl-formatter-tag-ok" type="major" >${normal}</el-tag>`;
  }
  if (params.data[6] === 'nr') {
    imgHtml = `
    <el-tag id="criticalCount" class="echarts-gl-formatter-tag-nr" type="major">${critical}</el-tag>`;
  }
  if (params.data[6] === 'cr') {
    imgHtml = `
    <el-tag
      id="majorCount"
      class="echarts-gl-formatter-tag-cr"
      type="major">${major}</el-tag>`;
  }
  if (params.data[6] === 'nc') {
    imgHtml = `
    <el-tag
      id="minorCount"
      class="echarts-gl-formatter-tag-nc"
      type="major">${minor}</el-tag>`;
  }
  return imgHtml;
}

function interpolation(
  origin: Data,
  width: number,
  height: number,
  step: number,
  degree: number,
): any {
  const value = new Float32Array(width * height * reciprocalUnitSq);
  const nonCriticalValue = new Float32Array(width * height * reciprocalUnitSq);
  const criticalValue = new Float32Array(width * height * reciprocalUnitSq);
  const nameList = new Array(width * height * reciprocalUnitSq).fill('');
  const statusList = new Array(width * height * reciprocalUnitSq).fill('');
  const discrete: any = [];
  Object.keys(origin).forEach((position: any) => {
    const pos = Number(position);
    const data = origin[pos][0] / step;
    const radius = Math.pow(data, 1 / degree);
    const radiusSq = Math.pow(radius, 2);

    const x = Math.floor(pos % width);
    const y = Math.floor(pos / width);

    discrete.push({ x, y, data: origin[pos], radius, radiusSq });
  });
  for (let i = 0; i < value.length; i++) {
    const { x, y } = float32ArrayPos2XY(width, i);
    const associatedNodes = getAssociatedNodes(discrete, x, y);

    if (associatedNodes.length === 0) {
      value[i] = 0;
      continue;
    }

    const overlayNode = hasNodeOverlap(associatedNodes, x, y);
    if (overlayNode) {
      value[i] = overlayNode.data[0];
      nonCriticalValue[i] = overlayNode.data[1];
      criticalValue[i] = overlayNode.data[2];
      nameList[i] = overlayNode.data[3];
      statusList[i] = overlayNode.data[4];
      continue;
    }
    const result = associatedNodes.map((node: any) => {
      const distance = getDistance(node.x, node.y, x, y);
      const temp = getNodeTemp(distance, node.data[0], step, degree);
      const weight = getWeight(distance);
      return { temp, weight };
    });
    value[i] = getFinalTemp(result);
  }
  return { value, nonCriticalValue, criticalValue, nameList, statusList };
}
function hasNodeOverlap(associatedNodes: any, x: any, y: any): INodeData {
  return associatedNodes.find((node: any) => {
    return node.x === x && node.y === y;
  });
}

function float32ArrayPosXY2(width: any, x: any, y: any): number {
  const pos = (x * reciprocalUnit) + (y * width * reciprocalUnitSq);
  return pos;
}

function float32ArrayPos2XY(width: any, pos: any): IFloatXY {
  const innerWidth = width * reciprocalUnit;
  const x = Math.floor(pos % innerWidth) * innerUnit;
  const y = Math.floor(pos / innerWidth) * innerUnit;
  return { x, y };
}

function getDistance(x1: any, y1: any, x2: any, y2: any): number {
  const xSq = Math.pow(x1 - x2, 2);
  const ySq = Math.pow(y1 - y2, 2);
  return Math.sqrt(xSq + ySq);
}

function getAssociatedNodes(discrete: any, x: any, y: any): any[] {
  return discrete.filter((node: any) => {
    return getDistance(node.x, node.y, x, y) < node.radius;
  });
}

function getNodeTemp(distance: any, data: any, step: any, degree: any): number {
  return data - (step * Math.pow(distance, degree));
}

function getWeight(distance: any): number {
  return 1 / Math.pow(distance, p);
}

function getFinalTemp(associatedNodes: any): number {
  let wt = 0;
  let wA = 0;
  associatedNodes.forEach((node: any) => {
    // 通过减少低温度点权重，减少凹陷 realWeight = weight * temp ^ 2
    const rW = node.weight * Math.pow(node.temp, 2);
    wA += rW;
    wt += rW * node.temp;
  });
  return wt / wA;
}

function float32Array2Data(data: any, width: any, height: any): object {
  const { value, nonCriticalValue, criticalValue, statusList, nameList } = data;
  const seriesData = [];
  seriesData.push([0, 0, 0, 0, 0, '', '']);
  let xMax = 0;
  let yMax = 0;
  xMax = Math.max(...value.map((item: any, i: number) => Math.floor(i % width)));
  yMax = Math.max(...value.map((item: any, i: number) => Math.floor(i / width)));
  for (let i = 0; i < value.length; i++) {
    const x = Math.floor(i % width);
    const y = Math.floor(i / width);
    if (y === 0 && x === 0) {
      for (let index = 0; index < xMax + 2; index++) {
        seriesData.push([index + 1, 0, 0, 0, 0, '', '']);
      }
    }
    if (x === 0) {
      seriesData.push([x, y + 1, 0, 0, 0, '', '']);
    }
    seriesData.push([
      x + 1,
      y + 1,
      value[i],
      nonCriticalValue[i],
      criticalValue[i],
      nameList[i],
      statusList[i],
    ]);
    if (x === xMax) {
      seriesData.push([x + 2, y + 1, 0, 0, 0, '', '']);
    }
  }
  for (let index = 0; index < xMax + 3; index++) {
    seriesData.push([index, yMax + 2, 0, 0, 0, '', '']);
  }
  return seriesData;
}
