/**
 * Created by Alex on 26-Feb-15.
 */

if (typeof window !== 'undefined') {
  window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
  window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
}

var util = require('../../util');


class CanvasRenderer {
  constructor(body, canvas) {
    this.body = body;
    this.canvas = canvas;

    this.redrawRequested = false;
    this.renderTimer = false;
    this.requiresTimeout = true;
    this.renderingActive = false;
    this.renderRequests = 0;
    this.pixelRatio = undefined;

    this.dragging = false;

    this.body.emitter.on("dragStart",       () => {this.dragging = true;});
    this.body.emitter.on("dragEnd",         () => this.dragging = false);
    this.body.emitter.on("_redraw",         () => {if (this.renderingActive === false) {this._redraw();}});
    this.body.emitter.on("_requestRedraw",  this._requestRedraw.bind(this));
    this.body.emitter.on("_startRendering", () => {this.renderRequests += 1; this.renderingActive = true; this.startRendering();});
    this.body.emitter.on("_stopRendering",  () => {this.renderRequests -= 1; this.renderingActive = this.renderRequests > 0;});

    this.options = {};
    this.defaultOptions = {
      hideEdgesOnDrag: false,
      hideNodesOnDrag: false
    }
    util.extend(this.options,this.defaultOptions);

    this._determineBrowserMethod();
  }

  setOptions(options) {
    if (options !== undefined) {
      util.deepExtend(this.options, options);
    }
  }

  startRendering() {
    if (this.renderingActive === true) {
      if (!this.renderTimer) {
        if (this.requiresTimeout === true) {
          this.renderTimer = window.setTimeout(this.renderStep.bind(this), this.simulationInterval); // wait this.renderTimeStep milliseconds and perform the animation step function
        }
        else {
          this.renderTimer = window.requestAnimationFrame(this.renderStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function
        }
      }
    }
    else {

    }
  }

  renderStep() {
    // reset the renderTimer so a new scheduled animation step can be set
    this.renderTimer = undefined;

    if (this.requiresTimeout === true) {
      // this schedules a new simulation step
      this.startRendering();
    }

    this._redraw();

    if (this.requiresTimeout === false) {
      // this schedules a new simulation step
      this.startRendering();
    }
  }

  /**
   * Redraw the network with the current data
   * chart will be resized too.
   */
  redraw() {
    this._redraw();
  }

  /**
   * Redraw the network with the current data
   * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over.
   * @private
   */
  _requestRedraw() {
    if (this.redrawRequested !== true && this.renderingActive === false) {
      this.redrawRequested = true;
      if (this.requiresTimeout === true) {
        window.setTimeout(this._redraw.bind(this, false),0);
      }
      else {
        window.requestAnimationFrame(this._redraw.bind(this, false));
      }
    }
  }

  _redraw(hidden = false) {
    this.body.emitter.emit("initRedraw");

    this.redrawRequested = false;
    var ctx = this.canvas.frame.canvas.getContext('2d');

    // when the container div was hidden, this fixes it back up!
    if (this.canvas.frame.canvas.width === 0 || this.canvas.frame.canvas.height === 0) {
      this.canvas.setSize();
    }

    if (this.pixelRation === undefined) {
      this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio ||
      ctx.mozBackingStorePixelRatio ||
      ctx.msBackingStorePixelRatio ||
      ctx.oBackingStorePixelRatio ||
      ctx.backingStorePixelRatio || 1);
    }

    ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);

    // clear the canvas
    var w = this.canvas.frame.canvas.clientWidth;
    var h = this.canvas.frame.canvas.clientHeight;
    ctx.clearRect(0, 0, w, h);



    this.body.emitter.emit("beforeDrawing", ctx);

    // set scaling and translation
    ctx.save();
    ctx.translate(this.body.view.translation.x, this.body.view.translation.y);
    ctx.scale(this.body.view.scale, this.body.view.scale);

    if (hidden === false) {
      if (this.dragging === false || (this.dragging === true && this.options.hideEdgesOnDrag === false)) {
        this._drawEdges(ctx);
      }
    }

    if (this.dragging === false || (this.dragging === true && this.options.hideNodesOnDrag === false)) {
      this._drawNodes(ctx, hidden);
    }

    if (this.controlNodesActive === true) {
      this._drawControlNodes(ctx);
    }

    //this.physics.nodesSolver._debug(ctx,"#F00F0F");

    this.body.emitter.emit("afterDrawing", ctx);

    // restore original scaling and translation
    ctx.restore();

    if (hidden === true) {
      ctx.clearRect(0, 0, w, h);
    }


  }


  /**
   * Redraw all nodes
   * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
   * @param {CanvasRenderingContext2D}   ctx
   * @param {Boolean} [alwaysShow]
   * @private
   */
  _drawNodes(ctx,alwaysShow = false) {
    var nodes = this.body.nodes;
    var nodeIndices = this.body.nodeIndices;
    var node;
    var selected = [];

    // draw unselected nodes;
    for (let i = 0; i < nodeIndices.length; i++) {
      node = nodes[nodeIndices[i]];
      // set selected nodes aside
      if (node.isSelected()) {
        selected.push(nodeIndices[i]);
      }
      else {
        if (alwaysShow === true) {
          node.draw(ctx);
        }
        // todo: replace check
        //else if (node.inArea() === true) {
          node.draw(ctx);
        //}
      }
    }

    // draw the selected nodes on top
    for (let i = 0; i < selected.length; i++) {
      node = nodes[selected[i]];
      node.draw(ctx);
    }
  }


  /**
   * Redraw all edges
   * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
   * @param {CanvasRenderingContext2D}   ctx
   * @private
   */
  _drawEdges(ctx) {
    var edges = this.body.edges;
    var edgeIndices = this.body.edgeIndices;
    var edge;

    for (let i = 0; i < edgeIndices.length; i++) {
      edge = edges[edgeIndices[i]];
      if (edge.connected === true) {
        edge.draw(ctx);
      }
    }
  }

  /**
   * Redraw all edges
   * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
   * @param {CanvasRenderingContext2D}   ctx
   * @private
   */
  _drawControlNodes(ctx) {
    var edges = this.body.edges;
    var edgeIndices = this.body.edgeIndices;
    var edge;

    for (let i = 0; i < edgeIndices.length; i++) {
      edge = edges[edgeIndices[i]];
      edge._drawControlNodes(ctx);
    }
  }

  /**
   * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because
   * some implementations (safari and IE9) did not support requestAnimationFrame
   * @private
   */
  _determineBrowserMethod() {
    if (typeof window !== 'undefined') {
      var browserType = navigator.userAgent.toLowerCase();
      this.requiresTimeout = false;
      if (browserType.indexOf('msie 9.0') != -1) { // IE 9
        this.requiresTimeout = true;
      }
      else if (browserType.indexOf('safari') != -1) {  // safari
        if (browserType.indexOf('chrome') <= -1) {
          this.requiresTimeout = true;
        }
      }
    }
    else {
      this.requiresTimeout = true;
    }
  }

}

export default CanvasRenderer;