import * as echarts from 'echarts/lib/echarts';
import graphicGL from '../../util/graphicGL';
import ViewGL from '../../core/ViewGL';
import Lines2DGeometry from '../../util/geometry/Lines2D';
import GLViewHelper from '../common/GLViewHelper';
import { getItemVisualColor, getItemVisualOpacity } from '../../util/visual';
import retrieve from '../../util/retrieve';
export default echarts.ChartView.extend({
  type: 'linesGL',
  __ecgl__: true,
  init: function (ecModel, api) {
    this.groupGL = new graphicGL.Node();
    this.viewGL = new ViewGL('orthographic');
    this.viewGL.add(this.groupGL);
    this._glViewHelper = new GLViewHelper(this.viewGL);
    this._nativeLinesShader = graphicGL.createShader('ecgl.lines3D');
    this._meshLinesShader = graphicGL.createShader('ecgl.meshLines3D');
    this._linesMeshes = [];
    this._currentStep = 0;
  },
  render: function (seriesModel, ecModel, api) {
    this.groupGL.removeAll();

    this._glViewHelper.reset(seriesModel, api);

    var linesMesh = this._linesMeshes[0];

    if (!linesMesh) {
      linesMesh = this._linesMeshes[0] = this._createLinesMesh(seriesModel);
    }

    this._linesMeshes.length = 1;
    this.groupGL.add(linesMesh);

    this._updateLinesMesh(seriesModel, linesMesh, 0, seriesModel.getData().count());

    this.viewGL.setPostEffect(seriesModel.getModel('postEffect'), api);
  },
  incrementalPrepareRender: function (seriesModel, ecModel, api) {
    this.groupGL.removeAll();

    this._glViewHelper.reset(seriesModel, api);

    this._currentStep = 0;
    this.viewGL.setPostEffect(seriesModel.getModel('postEffect'), api);
  },
  incrementalRender: function (params, seriesModel, ecModel, api) {
    var linesMesh = this._linesMeshes[this._currentStep];

    if (!linesMesh) {
      linesMesh = this._createLinesMesh(seriesModel);
      this._linesMeshes[this._currentStep] = linesMesh;
    }

    this._updateLinesMesh(seriesModel, linesMesh, params.start, params.end);

    this.groupGL.add(linesMesh);
    api.getZr().refresh();
    this._currentStep++;
  },
  updateTransform: function (seriesModel, ecModel, api) {
    if (seriesModel.coordinateSystem.getRoamTransform) {
      this._glViewHelper.updateTransform(seriesModel, api);
    }
  },
  _createLinesMesh: function (seriesModel) {
    var linesMesh = new graphicGL.Mesh({
      $ignorePicking: true,
      material: new graphicGL.Material({
        shader: graphicGL.createShader('ecgl.lines3D'),
        transparent: true,
        depthMask: false,
        depthTest: false
      }),
      geometry: new Lines2DGeometry({
        segmentScale: 10,
        useNativeLine: true,
        dynamic: false
      }),
      mode: graphicGL.Mesh.LINES,
      culling: false
    });
    return linesMesh;
  },
  _updateLinesMesh: function (seriesModel, linesMesh, start, end) {
    var data = seriesModel.getData();
    linesMesh.material.blend = seriesModel.get('blendMode') === 'lighter' ? graphicGL.additiveBlend : null;
    var curveness = seriesModel.get('lineStyle.curveness') || 0;
    var isPolyline = seriesModel.get('polyline');
    var geometry = linesMesh.geometry;
    var coordSys = seriesModel.coordinateSystem;
    var lineWidth = retrieve.firstNotNull(seriesModel.get('lineStyle.width'), 1);

    if (lineWidth > 1) {
      if (linesMesh.material.shader !== this._meshLinesShader) {
        linesMesh.material.attachShader(this._meshLinesShader);
      }

      linesMesh.mode = graphicGL.Mesh.TRIANGLES;
    } else {
      if (linesMesh.material.shader !== this._nativeLinesShader) {
        linesMesh.material.attachShader(this._nativeLinesShader);
      }

      linesMesh.mode = graphicGL.Mesh.LINES;
    }

    start = start || 0;
    end = end || data.count();
    geometry.resetOffset();
    var vertexCount = 0;
    var triangleCount = 0;
    var p0 = [];
    var p1 = [];
    var p2 = [];
    var p3 = [];
    var lineCoords = [];
    var t = 0.3;
    var t2 = 0.7;

    function updateBezierControlPoints() {
      p1[0] = p0[0] * t2 + p3[0] * t - (p0[1] - p3[1]) * curveness;
      p1[1] = p0[1] * t2 + p3[1] * t - (p3[0] - p0[0]) * curveness;
      p2[0] = p0[0] * t + p3[0] * t2 - (p0[1] - p3[1]) * curveness;
      p2[1] = p0[1] * t + p3[1] * t2 - (p3[0] - p0[0]) * curveness;
    }

    if (isPolyline || curveness !== 0) {
      for (var idx = start; idx < end; idx++) {
        if (isPolyline) {
          var count = seriesModel.getLineCoordsCount(idx);
          vertexCount += geometry.getPolylineVertexCount(count);
          triangleCount += geometry.getPolylineTriangleCount(count);
        } else {
          seriesModel.getLineCoords(idx, lineCoords);

          this._glViewHelper.dataToPoint(coordSys, lineCoords[0], p0);

          this._glViewHelper.dataToPoint(coordSys, lineCoords[1], p3);

          updateBezierControlPoints();
          vertexCount += geometry.getCubicCurveVertexCount(p0, p1, p2, p3);
          triangleCount += geometry.getCubicCurveTriangleCount(p0, p1, p2, p3);
        }
      }
    } else {
      var lineCount = end - start;
      vertexCount += lineCount * geometry.getLineVertexCount();
      triangleCount += lineCount * geometry.getLineVertexCount();
    }

    geometry.setVertexCount(vertexCount);
    geometry.setTriangleCount(triangleCount);
    var dataIndex = start;
    var colorArr = [];

    for (var idx = start; idx < end; idx++) {
      graphicGL.parseColor(getItemVisualColor(data, dataIndex), colorArr);
      var opacity = retrieve.firstNotNull(getItemVisualOpacity(data, dataIndex), 1);
      colorArr[3] *= opacity;
      var count = seriesModel.getLineCoords(idx, lineCoords);

      for (var k = 0; k < count; k++) {
        this._glViewHelper.dataToPoint(coordSys, lineCoords[k], lineCoords[k]);
      }

      if (isPolyline) {
        geometry.addPolyline(lineCoords, colorArr, lineWidth, 0, count);
      } else if (curveness !== 0) {
        p0 = lineCoords[0];
        p3 = lineCoords[1];
        updateBezierControlPoints();
        geometry.addCubicCurve(p0, p1, p2, p3, colorArr, lineWidth);
      } else {
        geometry.addPolyline(lineCoords, colorArr, lineWidth, 0, 2);
      }

      dataIndex++;
    }
  },
  dispose: function () {
    this.groupGL.removeAll();
  },
  remove: function () {
    this.groupGL.removeAll();
  }
});