"use strict";

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Abstract object representing window dimensions. We need to keep track of this information because
 * of computations involving picking, because of computations involving gui (i.e. minimap marker
 * position) and similar.
 *
 * @author smrceant (Antonin Smrcek)
 *
 * @param {number}               [width]                   width of the window (px)
 * @param {number}               [height]                  height of the window (px)
 * @param {function|function[]}  [windowResizeCallbacks]   functions executed when the window dimensions change
 * @constructor
 */
var Win = function (width, height, windowResizeCallbacks)
{
  /**
   * Width of the window (px).
   *
   * @type {number}
   */
  this.windowWidth  = null;

  /**
   * Height of the window (px).
   *
   * @type {number}
   */
  this.windowHeight = null;

  /**
   * How much will be dimensions of the window scaled (multiplied) when they are changed.
   *
   * Example: scale = 0.5, and we call setWindowSize() with width = 800, height = 600
   *          then resulting new windoth width will be width = 0.5 * 800 = 400 and height = 0.5 * 600 = 300
   * @type {number}
   */
  this.windowDimensionScale = this.__windowDimensionScale = 1;

  /**
   * When setting new window width, the current width is stored so the original window size can be restored
   * when necessary.
   *
   * @type {number}
   */
  this.windowWidthOld  = null;

  /**
   * When setting new window height, the current height is stored so the original window size can be restored
   * when necessary.
   *
   * @type {number}
   */
  this.windowHeightOld = null;

  /**
   * Ratio between window width and height (e.g. 1024x768 => 1024/768 = 1.33 = 4/3 = ratio called "4:3")
   *
   * @type {number}
   */
  this.windowRatio  = null;

  /**
   * Number of pixels within the window (windowWidth * windowHeight * 4 color channels)
   *
   * @type {number}
   */
  this.windowPixelCountRGBA = null;

  /**
   * Functions executed when the window dimensions are changed via setter.
   *
   * @type {function[]}
   */
  this.windowResizeCallbacks = Util.isInitialized(windowResizeCallbacks) ? ( (windowResizeCallbacks.constructor === Array) ? windowResizeCallbacks : [windowResizeCallbacks])
                                                                         : [];

  if (Util.isInitialized (width) && Util.isInitialized (height))
  {
    this.setWindowSize (width, height);
  }
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ADDING RESIZING CALLBACK, CHANGING WINDOW SIZE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Adds a callback to the list of callbacks which are executed when this windows resizes.
 *
 * @param {function}  callback    e.g. function which updates uniforms in shader with new resolution etc.
 */
Win.prototype.addResizingCallback = function (callback)
{
  this.windowResizeCallbacks.push (callback);
};

/**
 * Makes sure that the object is resized as well after the window is resized.
 * The argument is a callback which is used for changing the object size - it receives current
 * window dimensions in this function and is called.
 *
 * I'm sure it is clear, bude each objectResizeCallback registered by this function is called
 * after the window is resized.
 *
 * All created WebGLRenderTargets and this stuff shall use this.
 *
 * @param   {function}  objectResizeCallback    function used for changing dimensions of the object, it receives dimensions of this window
 */
Win.prototype.updateObjectAfterResize = function (objectResizeCallback)
{
  var _this = this;

  this.addResizingCallback (function()
  {
    objectResizeCallback (_this.windowWidth, _this.windowHeight);
  });
};

/**
 * Changes window dimensions, updates corresponding variables and executed associated callbacks.
 *
 * Possible arguments:
 *
 * nothing     - will use current values of window dimensions for update
 * 1 argument  - array with width at [0] and height at [1]
 * 2 arguments - width and height

 * @param {undefined|number[]|*} [inputArgs]    nothing, array with width and height or width and height
 */
Win.prototype.setWindowSize = function (inputArgs)
{
  if (arguments.length === 0) // no passed arguments
  {
    var width  = this.windowWidth;
    var height = this.windowHeight;
  }
  else
  {
    if (arguments.length === 1) // 1 passed argument
    {
      var dimensions = arguments[0];

      if (dimensions.constructor === Array)
      {
        var width  = dimensions[0];
        var height = dimensions[1];
      }
      else
      {
        console.error("You probably forgot to pass height in setWindowSize().");

        // try to continue with at least some sensible values for height
        var width  = dimensions[0];
        var height = width;
      }
    }
    else // 2 passed arguments
    {
      var width  = arguments[0];
      var height = arguments[1];
    }
  }

  width  *= this.windowDimensionScale;
  height *= this.windowDimensionScale;

  this.windowWidthOld  = (this.windowWidth  !== null) ? this.windowWidth  : width;
  this.windowHeightOld = (this.windowHeight !== null) ? this.windowHeight : height;

  this.windowWidth  = width;
  this.windowHeight = height;

  this.windowPixelCountRGBA = width * height * 4;

  this.windowRatio = Win.computeWindowRatio (width, height);

  for (var i = 0; i < this.windowResizeCallbacks.length; i++)
  {
    this.windowResizeCallbacks[i]();
  }
};

/**
 * Changes window dimensions to the previous values.
 */
Win.prototype.restoreOldWindowSize = function()
{
  this.setWindowSize (this.windowWidthOld, this.windowHeightOld);
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// COMPUTING WINDOW RATIO
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Computes ratio between two window's width and height. The ratio will be computed as original/another.
 *
 * @param {Win}             original  e.g. 640x240 window
 * @param {Win}             another   e.g. 1024x960 window
 * @param {THREE.Vector2}   out       output vector for the resulting width and height ratio, e.g. (0.5, 0.25)
 */
Win.computeRatioToAnotherWindow = function (original, another, out)
{
  if (Util.isInitialized (original) && Util.isInitialized (another))
  {
    out.x = original.windowWidth  / another.windowWidth;
    out.y = original.windowHeight / another.windowHeight;
  }
};

/**
 * Computes (aspect) ratio between window width and height.
 *
 * @param   {number}    width   horizontal size of the window in px
 * @param   {height}    height  vertical size of the window in px
 * @returns {number}            width:height ratio
 */
Win.computeWindowRatio  = function (width, height)
{
  return (width / height);
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
