"use strict";

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

/**
 * Represents minimap (and map) displayed within the on-screen GUI - implementation point of view,
 * i.e. computing coordinates, correct scale and similar.
 *
 * @author smrceant (Antonin Smrcek)
 *
 * @param          parentMap        either Bigmap or Minimap instance
 * @param          [underlyingMap]  underlying map object (contains view-projection matrix used to capture the map etc.)
 * @constructor
 */
var MapGui = function (parentMap, underlyingMap)
{
  /**
   *
   * @type {Bigmap|Minimap}
   */
  this.parentMap = parentMap;

  /**
   * Current scale (zoom) of the minimap.
   * Examples: original image size = 1.0, 2x zoomed in = 2.0, 2x zoomed out = 0.5 etc.
   *
   * @type {number}
   */
  this.mapScale = 1.0;

  /**
   *
   * @type {number}
   */
  this.mapAbsoluteScale = null;
  this.computeAbsoluteMinimapScale();

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // UNDERLYING MAP
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  /**
   * Coordinate of controlling entity to the map.
   *
   * @type {THREE.Vector2}
   * @private
   */
  this._tmp_mapCoord = new THREE.Vector2();

  /**
   * Coordinates to the image.
   *
   * @type {THREE.Vector2}
   * @private
   */
  this._tmp_imgCoord = new THREE.Vector2();

  /**
   * Used for coordinate computation into the minimap and big map.
   *
   * @type {Map}
   */
  this.underlyingMap = underlyingMap;

  // (world heightmap must be already created in time of calling constructor)
  if (!Util.isInitialized (underlyingMap))
  {
    if (Preferences.Map.USE_WORLD_HEIGHTMAP_WIDTH_HEIGHT_AND_MATRIX)
    {
      this.underlyingMap = heightmapWorld.map;
    }
    else
    {
      console.error("Map not using heightmap parameters is not supported as of now.");

      this.underlyingMap = heightmapWorld.map;
    }
  }
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// EVENTS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Called upon browser window resize.
 *
 * @private
 */
MapGui.prototype._resizeCallback = function()
{
  this.computeAbsoluteMinimapScale();

  // update only if not running application, otherwise it will be updated in main loop
  if (!Preferences.Dev.RUN_APPLICATION && Preferences.Application.isDevMode())
  {
    this.parentMap.updateMinimap (player);
  }
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ZOOMING
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Zooms the map in (computes correct scale and updates parent Big map / Minimap object).
 */
MapGui.prototype.zoomIn = function()
{
  if (this._scaleMinimap (Preferences.Map.MINIMAP_SCALE_FACTOR))
  {
    this.parentMap.updateMinimap (player);
  }
};

/**
 * Zooms the map out (computes correct scale and updates parent Big map / Minimap object).
 */
MapGui.prototype.zoomOut = function()
{
  if (this._scaleMinimap (1 / Preferences.Map.MINIMAP_SCALE_FACTOR))
  {
    this.parentMap.updateMinimap (player);
  }
};

/**
 * Scales the minimap by given factor. If the resulting scale would be below the allowed minimum/above the maximum limit,
 * then no scaling is performed.
 *
 * @param   {number} scaleFactor    float representing how much will be map smaller (number < 1.0) or bigger (number > 1.0)
 * @returns {boolean}               TRUE if the map was scaled
 */
MapGui.prototype._scaleMinimap = function (scaleFactor)
{
  var oldScale = this.mapScale;

  this.mapScale *= scaleFactor;

  if ((this.mapScale < Preferences.Map.MINIMAP_MIN_SCALE) || ((this.mapScale > Preferences.Map.MINIMAP_MAX_SCALE)))
  {
    this.mapScale = oldScale;

    return false;
  }

  this.computeAbsoluteMinimapScale();

  return true;
};

/**
 * Computes value of scale which will be written into the map CSS element.
 */
MapGui.prototype.computeAbsoluteMinimapScale = function ()
{
  this.mapAbsoluteScale = this.mapScale * (1/gui.windowDisplayScaleRatio.y); // 1/ because the ratio is computed inversely now
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// UPDATING THE MINIMAP IMAGE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Computes 2D coordinates to the map image from entity's 3D coordinates.
 *
 * @param   {Entity}          entity          entity for whose 3D position we are computing the 2D coordinates in map image
 * @param   {boolean}         scalePosition   TRUE if the position shall be also scaled
 * @returns {THREE.Vector2}
 */
MapGui.prototype.computeCoordinatesInMapImage = function (entity, scalePosition)
{
  this.underlyingMap.computeScreenCoordinates (entity.getPosition(), this._tmp_mapCoord);

  // OpenGL has different coordinate system than screen (Y is inversed), so flip the Y value to get correct coordinates in the minimap image (on the screen)
  this._tmp_imgCoord.x = this._tmp_mapCoord.x;
  this._tmp_imgCoord.y = this.underlyingMap.height - this._tmp_mapCoord.y;

	if (scalePosition)
	{
		// also scale the position depending on current minimap scale (assumption: uniform scale)
		this._tmp_imgCoord.multiplyScalar (this.mapAbsoluteScale);

		this._tmp_imgCoord.x = Math.round (this._tmp_imgCoord.x);
		this._tmp_imgCoord.y = Math.round (this._tmp_imgCoord.y);
	}

  return this._tmp_imgCoord;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// GETTERS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Retrieves coordinates into the map image which were computed as last.
 *
 * @returns {THREE.Vector2}
 */
MapGui.prototype.getLastCoordinatesInMap = function()
{
  return this._tmp_mapCoord;
};

/**
 * Retrieves underlying map object.
 *
 * @returns {Map}
 */
MapGui.prototype.getUnderlyingMap = function()
{
  return this.underlyingMap;
};
