(function() {
  'use strict';
  // calculate pixel ratio

  var _pixelRatio;
  function getDevicePixelRatio() {
    if (_pixelRatio) {
      return _pixelRatio;
    }
    var canvas = Konva.Util.createCanvasElement();
    var context = canvas.getContext('2d');
    _pixelRatio = (function() {
      var devicePixelRatio = Konva.window.devicePixelRatio || 1,
        backingStoreRatio =
          context.webkitBackingStorePixelRatio ||
          context.mozBackingStorePixelRatio ||
          context.msBackingStorePixelRatio ||
          context.oBackingStorePixelRatio ||
          context.backingStorePixelRatio ||
          1;
      return devicePixelRatio / backingStoreRatio;
    })();
    return _pixelRatio;
  }

  /**
   * Canvas Renderer constructor
   * @constructor
   * @abstract
   * @memberof Konva
   * @param {Object} config
   * @param {Number} config.width
   * @param {Number} config.height
   * @param {Number} config.pixelRatio KonvaJS automatically handles pixel ratio adjustments in order to render crisp drawings
   *  on all devices. Most desktops, low end tablets, and low end phones, have device pixel ratios
   *  of 1.  Some high end tablets and phones, like iPhones and iPads (not the mini) have a device pixel ratio
   *  of 2.  Some Macbook Pros, and iMacs also have a device pixel ratio of 2.  Some high end Android devices have pixel
   *  ratios of 2 or 3.  Some browsers like Firefox allow you to configure the pixel ratio of the viewport.  Unless otherwise
   *  specified, the pixel ratio will be defaulted to the actual device pixel ratio.  You can override the device pixel
   *  ratio for special situations, or, if you don't want the pixel ratio to be taken into account, you can set it to 1.
   */
  Konva.Canvas = function(config) {
    this.init(config);
  };

  Konva.Canvas.prototype = {
    init: function(config) {
      var conf = config || {};

      var pixelRatio =
        conf.pixelRatio || Konva.pixelRatio || getDevicePixelRatio();

      this.pixelRatio = pixelRatio;
      this._canvas = Konva.Util.createCanvasElement();

      // set inline styles
      this._canvas.style.padding = 0;
      this._canvas.style.margin = 0;
      this._canvas.style.border = 0;
      this._canvas.style.background = 'transparent';
      this._canvas.style.position = 'absolute';
      this._canvas.style.top = 0;
      this._canvas.style.left = 0;
    },
    /**
     * get canvas context
     * @method
     * @memberof Konva.Canvas.prototype
     * @returns {CanvasContext} context
     */
    getContext: function() {
      return this.context;
    },
    /**
     * get pixel ratio
     * @method
     * @memberof Konva.Canvas.prototype
     * @returns {Number} pixel ratio
     */
    getPixelRatio: function() {
      return this.pixelRatio;
    },
    /**
     * get pixel ratio
     * @method
     * @memberof Konva.Canvas.prototype
     * @param {Number} pixelRatio KonvaJS automatically handles pixel ratio adustments in order to render crisp drawings
     *  on all devices. Most desktops, low end tablets, and low end phones, have device pixel ratios
     *  of 1.  Some high end tablets and phones, like iPhones and iPads have a device pixel ratio
     *  of 2.  Some Macbook Pros, and iMacs also have a device pixel ratio of 2.  Some high end Android devices have pixel
     *  ratios of 2 or 3.  Some browsers like Firefox allow you to configure the pixel ratio of the viewport.  Unless otherwise
     *  specificed, the pixel ratio will be defaulted to the actual device pixel ratio.  You can override the device pixel
     *  ratio for special situations, or, if you don't want the pixel ratio to be taken into account, you can set it to 1.
     */
    setPixelRatio: function(pixelRatio) {
      var previousRatio = this.pixelRatio;
      this.pixelRatio = pixelRatio;
      this.setSize(
        this.getWidth() / previousRatio,
        this.getHeight() / previousRatio
      );
    },
    /**
     * set width
     * @method
     * @memberof Konva.Canvas.prototype
     * @param {Number} width
     */
    setWidth: function(width) {
      // take into account pixel ratio
      this.width = this._canvas.width = width * this.pixelRatio;
      this._canvas.style.width = width + 'px';

      var pixelRatio = this.pixelRatio,
        _context = this.getContext()._context;
      _context.scale(pixelRatio, pixelRatio);
    },
    /**
     * set height
     * @method
     * @memberof Konva.Canvas.prototype
     * @param {Number} height
     */
    setHeight: function(height) {
      // take into account pixel ratio
      this.height = this._canvas.height = height * this.pixelRatio;
      this._canvas.style.height = height + 'px';
      var pixelRatio = this.pixelRatio,
        _context = this.getContext()._context;
      _context.scale(pixelRatio, pixelRatio);
    },
    /**
     * get width
     * @method
     * @memberof Konva.Canvas.prototype
     * @returns {Number} width
     */
    getWidth: function() {
      return this.width;
    },
    /**
     * get height
     * @method
     * @memberof Konva.Canvas.prototype
     * @returns {Number} height
     */
    getHeight: function() {
      return this.height;
    },
    /**
     * set size
     * @method
     * @memberof Konva.Canvas.prototype
     * @param {Number} width
     * @param {Number} height
     */
    setSize: function(width, height) {
      this.setWidth(width);
      this.setHeight(height);
    },
    /**
     * to data url
     * @method
     * @memberof Konva.Canvas.prototype
     * @param {String} mimeType
     * @param {Number} quality between 0 and 1 for jpg mime types
     * @returns {String} data url string
     */
    toDataURL: function(mimeType, quality) {
      try {
        // If this call fails (due to browser bug, like in Firefox 3.6),
        // then revert to previous no-parameter image/png behavior
        return this._canvas.toDataURL(mimeType, quality);
      } catch (e) {
        try {
          return this._canvas.toDataURL();
        } catch (err) {
          Konva.Util.warn('Unable to get data URL. ' + err.message);
          return '';
        }
      }
    }
  };

  Konva.SceneCanvas = function(config) {
    var conf = config || {};
    var width = conf.width || 0,
      height = conf.height || 0;

    Konva.Canvas.call(this, conf);
    this.context = new Konva.SceneContext(this);
    this.setSize(width, height);
  };

  Konva.Util.extend(Konva.SceneCanvas, Konva.Canvas);

  Konva.HitCanvas = function(config) {
    var conf = config || {};
    var width = conf.width || 0,
      height = conf.height || 0;

    Konva.Canvas.call(this, conf);
    this.context = new Konva.HitContext(this);
    this.setSize(width, height);
    this.hitCanvas = true;
  };
  Konva.Util.extend(Konva.HitCanvas, Konva.Canvas);
})();
