"use strict";

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

/**
 * Information about viewpoint - camera position, orientation, optionally name of the viewpoint with a screenshot.
 * @author smrceant (Antonin Smrcek)
 *
 * @param {THREE.Vector3}     position                camera position in the world
 * @param {number}            angleHorizontal         horizontal camera orientation in radians in range [0,2PI), i.e. left/right direction
 * @param {number}            angleVertical           vertical camera orientation in radians in range [0,PI), i.e. up/down direction
 * @param {string}            [name]                  internal name of the viewpoint, without any prefix; caption displayed above the minimap or in the viewpoint selection will be based on this name (caption defined in Content Strings)
 * @param {function}          [activationCallback]    function executed after the viewpoint is activated
 * @constructor
 */
var Viewpoint = function (position, angleHorizontal, angleVertical, name, activationCallback)
{
  /**
   * Viewpoint position in the world. Entity's camera will receive this position.
   *
   * @type {THREE.Vector3}
   */
  this.position        = position;

  /**
   * Position of the viewpoint in the Big map and Minimap. Computed using projection.
   *
   * @type {THREE.Vector2}
   */
  this.position2dInMap = new THREE.Vector2();

  /**
   * Horizontal camera orientation in radians in range [0,2PI), i.e. left/right direction.
   *
   * @type {number}
   */
  this.angleHorizontal = angleHorizontal;

  /**
   * Vertical camera orientation in radians in range [0,PI), i.e. up/down direction.
   *
   * @type {number}
   */
  this.angleVertical   = angleVertical;

  /**
   * Function executed after the viewpoint is activated.
   *
   * @type {Function}
   */
  this.activationCallback = activationCallback;

  /////////////////////////////////////////////////////////////
  // INTERNAL NAME CHECK, SNAPSHOT FILENAME CONSTRUCTION
  /////////////////////////////////////////////////////////////

  if (!Util.isInitialized (name))
  {
    name = "unspecified";
  }

  ///////////////////////////////////////////////////////////////////
  // << MAP INTERFACE >> (used by Viewpoint, ObjectInfo)
  ///////////////////////////////////////////////////////////////////

  /**
   * Internal viewpoint location name.
   *
   * @type {string} e.g. "courtyard1"
   */
  this.name = name;

  /**
   * Retrieves content string identificator with caption for this location.
   *
   * @type {string}
   */
  this.caption = this.name.toUpperCase();

  /**
   * Image file with the viewpoint snapshot (full path, e.g. "data/gui/snapshots/viewpoint_courtyard1.jpg").
   *
   * @type {string}
   */
  this.snapshotImageFilename = Preferences.Gui.getLocationSnapshotFilepath (this.name);
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// COMPUTING POSITIONS OF VIEWPOINTS IN THE MAPS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Computes position of public viewpoints (i.e. the visible ones in the application in the Viewpoint window).
 * Must be done after the Minimap is initialized.
 *
 * @param {Map} underlyingMap   map with view-projection matrix used for capturing the map (will be used for projection)
 */
Viewpoint.computePositions2dInMap = function (underlyingMap)
{
  for (var i = 0; i < Preferences.Viewpoint.getPublicViewpoints().length; i++)
  {
    Preferences.Viewpoint.getPublicViewpoints()[i].computePosition2dInMap (underlyingMap);
  }
};

/**
 * Computes position of the viewpoint in the (big and mini) map.
 *
 * @param {Map} underlyingMap   map with view-projection matrix used for capturing the map (will be used for projection)
 */
Viewpoint.prototype.computePosition2dInMap = function (underlyingMap)
{
  underlyingMap.computeScreenCoordinates (this.position, this.position2dInMap);

  // to screen coordinates
  this.position2dInMap.y = underlyingMap.height - this.position2dInMap.y;
};

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

/**
 *
 * @returns {THREE.Vector3}    camera position in the viewpoint
 */
Viewpoint.prototype.getPosition = function()
{
  // deep copy is needed, otherwise we could unintentionally modify this viewpoint
  return this.position.clone();
};

/**
 *
 * @returns {number}  horizontal camera orientation in the viewpoint (the angle is called zenith or something like that)
 */
Viewpoint.prototype.getAngleHorizontal = function()
{
  return this.angleHorizontal;
};

/**
 *
 * @returns {number}    vertical camera orientation in the viewpoint (azimuth)
 */
Viewpoint.prototype.getAngleVertical = function()
{
  return this.angleVertical;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// << MAP INTERFACE >> (used by Viewpoint, ObjectInfo)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 * Retrieves internal name of the viewpoint.
 *
 * @returns {string}  e.g. "viewpoint_courtyard1"
 */
Viewpoint.prototype.getName = function()
{
  return this.name;
};

/**
 * Retrieves content string identificator with caption for this location.
 *
 * @returns {string}
 */
Viewpoint.prototype.getCaption = function()
{
  return this.caption;
};

/**
 * Retrieves filename with screenshot.
 *
 * @returns {string}  image file (full path) with the location screenshot.
 */
Viewpoint.prototype.getSnapshotImageFilename = function()
{
  return this.snapshotImageFilename;
};

/**
 * Moves (camera of the) current application entity to this viewpoint.
 */
Viewpoint.prototype.setAsCurrentViewpoint = function()
{
  appMain.getCurrentEntity().camera.switchViewpoint (this);

  if (Util.isInitialized (this.activationCallback))
  {
    this.activationCallback();
  }
};
