// 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 { COORDINATE_SYSTEM, Layer, experimental } from '../../core';
const fp64LowPart = experimental.fp64LowPart,
      enable64bitSupport = experimental.enable64bitSupport;


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

import vs from './arc-layer-vertex.glsl';
import vs64 from './arc-layer-vertex-64.glsl';
import fs from './arc-layer-fragment.glsl';

const DEFAULT_COLOR = [0, 0, 0, 255];

const defaultProps = {
  strokeWidth: 1,
  fp64: false,

  getSourcePosition: x => x.sourcePosition,
  getTargetPosition: x => x.targetPosition,
  getSourceColor: x => x.color || DEFAULT_COLOR,
  getTargetColor: x => x.color || DEFAULT_COLOR
};

export default class ArcLayer extends Layer {
  getShaders() {
    return enable64bitSupport(this.props) ? { vs: vs64, fs, modules: ['project64', 'picking'] } : { vs, fs, modules: ['picking'] }; // 'project' module added by default.
  }

  initializeState() {
    const attributeManager = this.getAttributeManager();

    /* eslint-disable max-len */
    attributeManager.addInstanced({
      instancePositions: {
        size: 4,
        transition: true,
        accessor: ['getSourcePosition', 'getTargetPosition'],
        update: this.calculateInstancePositions
      },
      instanceSourceColors: {
        size: 4,
        type: GL.UNSIGNED_BYTE,
        transition: true,
        accessor: 'getSourceColor',
        update: this.calculateInstanceSourceColors
      },
      instanceTargetColors: {
        size: 4,
        type: GL.UNSIGNED_BYTE,
        transition: true,
        accessor: 'getTargetColor',
        update: this.calculateInstanceTargetColors
      }
    });
    /* eslint-enable max-len */
  }

  updateAttribute({ props, oldProps, changeFlags }) {
    if (props.fp64 !== oldProps.fp64) {
      const attributeManager = this.getAttributeManager();
      attributeManager.invalidateAll();

      if (props.fp64 && props.coordinateSystem === COORDINATE_SYSTEM.LNGLAT) {
        attributeManager.addInstanced({
          instancePositions64Low: {
            size: 4,
            accessor: ['getSourcePosition', 'getTargetPosition'],
            update: this.calculateInstancePositions64Low
          }
        });
      } else {
        attributeManager.remove(['instancePositions64Low']);
      }
    }
  }

  updateState({ props, oldProps, changeFlags }) {
    super.updateState({ props, oldProps, changeFlags });
    // Re-generate model if geometry changed
    if (props.fp64 !== oldProps.fp64) {
      const gl = this.context.gl;

      if (this.state.model) {
        this.state.model.delete();
      }
      this.setState({ model: this._getModel(gl) });
    }
    this.updateAttribute({ props, oldProps, changeFlags });
  }

  draw({ uniforms }) {
    const strokeWidth = this.props.strokeWidth;


    this.state.model.render(Object.assign({}, uniforms, {
      strokeWidth
    }));
  }

  _getModel(gl) {
    let positions = [];
    const NUM_SEGMENTS = 50;
    /*
     *  (0, -1)-------------_(1, -1)
     *       |          _,-"  |
     *       o      _,-"      o
     *       |  _,-"          |
     *   (0, 1)"-------------(1, 1)
     */
    for (let i = 0; i < NUM_SEGMENTS; i++) {
      positions = positions.concat([i, -1, 0, i, 1, 0]);
    }

    const model = new Model(gl, Object.assign({}, this.getShaders(), {
      id: this.props.id,
      geometry: new Geometry({
        drawMode: GL.TRIANGLE_STRIP,
        attributes: {
          positions: new Float32Array(positions)
        }
      }),
      isInstanced: true,
      shaderCache: this.context.shaderCache
    }));

    model.setUniforms({ numSegments: NUM_SEGMENTS });

    return model;
  }

  calculateInstancePositions(attribute) {
    var _props = this.props;
    const data = _props.data,
          getSourcePosition = _props.getSourcePosition,
          getTargetPosition = _props.getTargetPosition;
    const value = attribute.value,
          size = attribute.size;

    let i = 0;
    for (const object of data) {
      const sourcePosition = getSourcePosition(object);
      const targetPosition = getTargetPosition(object);
      value[i + 0] = sourcePosition[0];
      value[i + 1] = sourcePosition[1];
      value[i + 2] = targetPosition[0];
      value[i + 3] = targetPosition[1];
      i += size;
    }
  }

  calculateInstancePositions64Low(attribute) {
    var _props2 = this.props;
    const data = _props2.data,
          getSourcePosition = _props2.getSourcePosition,
          getTargetPosition = _props2.getTargetPosition;
    const value = attribute.value,
          size = attribute.size;

    let i = 0;
    for (const object of data) {
      const sourcePosition = getSourcePosition(object);
      const targetPosition = getTargetPosition(object);
      value[i + 0] = fp64LowPart(sourcePosition[0]);
      value[i + 1] = fp64LowPart(sourcePosition[1]);
      value[i + 2] = fp64LowPart(targetPosition[0]);
      value[i + 3] = fp64LowPart(targetPosition[1]);
      i += size;
    }
  }

  calculateInstanceSourceColors(attribute) {
    var _props3 = this.props;
    const data = _props3.data,
          getSourceColor = _props3.getSourceColor;
    const value = attribute.value,
          size = attribute.size;

    let i = 0;
    for (const object of data) {
      const color = getSourceColor(object);
      value[i + 0] = color[0];
      value[i + 1] = color[1];
      value[i + 2] = color[2];
      value[i + 3] = isNaN(color[3]) ? 255 : color[3];
      i += size;
    }
  }

  calculateInstanceTargetColors(attribute) {
    var _props4 = this.props;
    const data = _props4.data,
          getTargetColor = _props4.getTargetColor;
    const value = attribute.value,
          size = attribute.size;

    let i = 0;
    for (const object of data) {
      const color = getTargetColor(object);
      value[i + 0] = color[0];
      value[i + 1] = color[1];
      value[i + 2] = color[2];
      value[i + 3] = isNaN(color[3]) ? 255 : color[3];
      i += size;
    }
  }
}

ArcLayer.layerName = 'ArcLayer';
ArcLayer.defaultProps = defaultProps;
//# sourceMappingURL=arc-layer.js.map