// Copyright (c) 2015 - 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import {Layer} from '../../../lib';
import {GL, Model, Geometry} from 'luma.gl';

import vs from './screen-grid-layer-vertex.glsl';
import fs from './screen-grid-layer-fragment.glsl';

const defaultProps = {
  cellSizePixels: 100,

  // Color range?
  minColor: [0, 0, 0, 255],
  maxColor: [0, 255, 0, 255],

  getPosition: d => d.position,
  getWeight: d => 1
};

export default class ScreenGridLayer extends Layer {
  getShaders() {
    return {vs, fs, modules: ['picking']}; // 'project' module added by default.
  }

  constructor(props) {
    super(props);
    this._checkRemovedProp('unitWidth', 'cellSizePixels');
    this._checkRemovedProp('unitHeight', 'cellSizePixels');
  }

  initializeState() {
    const {attributeManager} = this.state;
    const {gl} = this.context;

    /* eslint-disable max-len */
    attributeManager.addInstanced({
      instancePositions: {size: 3, update: this.calculateInstancePositions},
      instanceCount: {size: 1, accessor: ['getPosition', 'getWeight'], update: this.calculateInstanceCount}
    });
    /* eslint-disable max-len */

    this.setState({model: this._getModel(gl)});
  }

  shouldUpdateState({changeFlags}) {
    return changeFlags.somethingChanged;
  }

  updateState({oldProps, props, changeFlags}) {
    super.updateState({props, oldProps, changeFlags});
    const cellSizeChanged =
      props.cellSizePixels !== oldProps.cellSizePixels;

    if (cellSizeChanged || changeFlags.viewportChanged) {
      this.updateCell();
    }
  }

  draw({uniforms}) {
    const {minColor, maxColor} = this.props;
    const {model, cellScale, maxCount} = this.state;
    uniforms = Object.assign({}, uniforms, {minColor, maxColor, cellScale, maxCount});
    model.draw({
      uniforms,
      parameters: {
        depthMask: true
      }
    });
  }

  _getModel(gl) {
    return new Model(gl, Object.assign({}, this.getShaders(), {
      id: this.props.id,
      geometry: new Geometry({
        drawMode: GL.TRIANGLE_FAN,
        vertices: new Float32Array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0])
      }),
      isInstanced: true,
      shaderCache: this.context.shaderCache
    }));
  }

  updateCell() {
    const {width, height} = this.context.viewport;
    const {cellSizePixels} = this.props;

    const MARGIN = 2;
    const cellScale = new Float32Array([
      (cellSizePixels - MARGIN) / width * 2,
      -(cellSizePixels - MARGIN) / height * 2,
      1
    ]);
    const numCol = Math.ceil(width / cellSizePixels);
    const numRow = Math.ceil(height / cellSizePixels);

    this.setState({
      cellScale,
      numCol,
      numRow,
      numInstances: numCol * numRow
    });

    const {attributeManager} = this.state;
    attributeManager.invalidateAll();
  }

  calculateInstancePositions(attribute, {numInstances}) {
    const {width, height} = this.context.viewport;
    const {cellSizePixels} = this.props;
    const {numCol} = this.state;
    const {value, size} = attribute;

    for (let i = 0; i < numInstances; i++) {
      const x = i % numCol;
      const y = Math.floor(i / numCol);
      value[i * size + 0] = x * cellSizePixels / width * 2 - 1;
      value[i * size + 1] = 1 - y * cellSizePixels / height * 2;
      value[i * size + 2] = 0;
    }
  }

  calculateInstanceCount(attribute) {
    const {data, cellSizePixels, getPosition, getWeight} = this.props;
    const {numCol, numRow} = this.state;
    const {value} = attribute;
    let maxCount = 0;

    value.fill(0.0);

    for (const point of data) {
      const pixel = this.project(getPosition(point));
      const colId = Math.floor(pixel[0] / cellSizePixels);
      const rowId = Math.floor(pixel[1] / cellSizePixels);
      if (colId >= 0 && colId < numCol && rowId >= 0 && rowId < numRow) {
        const i = colId + rowId * numCol;
        value[i] += getWeight(point);
        if (value[i] > maxCount) {
          maxCount = value[i];
        }
      }
    }

    this.setState({maxCount});
  }
}

ScreenGridLayer.layerName = 'ScreenGridLayer';
ScreenGridLayer.defaultProps = defaultProps;

@@ -2669,16 +2669,33 @@
 maxColor
+, parameters = %7B%7D
 %7D = this
@@ -2893,18 +2893,58 @@
 meters: 
+Object.assign(
 %7B%0A
+        depthTest: false,%0A
         
@@ -2954,19 +2954,20 @@
 thMask: 
-tru
+fals
 e%0A      
@@ -2963,24 +2963,37 @@
 alse%0A      %7D
+, parameters)
 %0A    %7D);%0A  %7D

Add depthTest:false to ScreenGridLayer (#875)