"use strict";

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

/**
 * Creates main menu (part of GUI) of the application. Initializes jQuery UI Tab widget used for main menu, fills it
 * with text in current application language. HTML markup of the main menu is in the file mainmenu.html, and it is
 * guaranteed that the file is fully loaded when the constructor is executed.
 *
 * @author smrceant (Antonin Smrcek)
 *
 * @constructor
 */
var MainMenu = function()
{
  var _this = this;

  /**
   * TRUE when the menu is opened.
   *
   * @type {boolean}
   */
  this.visible = false;

  /////////////////////////////////////////////////////////////////
  // JQUERY ELEMENTS CACHING
  /////////////////////////////////////////////////////////////////

  this.$mainMenu_elem = $("#mainMenu");
  gui.$onscreenGui_elem.append (this.$mainMenu_elem);

  // close button with the cross image
  this.$mainMenuClose_elem = $("#mainMenuClose");

  this.$mainMenuCloseButtonIcon_elem = $("#mainMenuCloseButtonIcon");
  this.$mainMenuCloseButton_elem = $("#mainMenuCloseButton");

  /////////////////////////////////////////////////////////////////
  // VARIOUS INITALIZATIONS
  /////////////////////////////////////////////////////////////////

  this.initSliders();

  this.initSelectMenuForResolution();

  this._initTabWidget();

  this._initOptionLinkWithApplicationVariables();

  this.initTransformLinksToTabs();

  this._initButtons();

  this.initMenuWidgetVariousCss();

  /////////////////////////////////////////////////////////////////
  // REGISTERING EVENT CALLBACKS TO THE MENU WIDGET
  /////////////////////////////////////////////////////////////////

  this.$mainMenu_elem.keydown (function (event)
  {
    if (event.which === KeyEvent.DOM_VK_ESCAPE)
    {
      event.preventDefault();

      _this.hide();
    }
  });

  /////////////////////////////////////////////////////////////////
  // FILLING MENU WITH TEXT IN CURRENT LANGUAGE
  /////////////////////////////////////////////////////////////////

  // must be created after all elements are inserted into the DOM!
	this.contentObject = new ContentObject (
		this.$mainMenu_elem,
		{},
		null,
		function() // abuse additional translation callback mechanism to update scrollbar after each translation :)
		{}
	);

  this.contentObject.translate();

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

  this.hide();
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// INITIALIZATION
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Initializes perfect scrollbar on each tab content page, adds various CSS attributes needed for
 * correct menu functionality etc.
 *
 * @private
 */
MainMenu.prototype.initMenuWidgetVariousCss = function()
{
  ///////////////////////////////////////////////////////////////////////////
  // MENU WIDGET CSS - ADDING ATTRIBUTES
  ///////////////////////////////////////////////////////////////////////////

  // (improvement: rename HTML attributes to lowercase, cause jQuery converts it automatically to it?)

  // add id of the correct tab content <div> as an attribute to each tab icon
  $("*[id*=tabOption]").each (function()
  {
    var id = $(this).attr("id");
    var optionName = id.substring (id.indexOf("_"));

    $(this).attr("tabContentName", "tabContent" + optionName);
  });

  ///////////////////////////////////////////////////////////////////////////
  // MODAL OVERLAY BACKGROUND
  ///////////////////////////////////////////////////////////////////////////

  // gray area behind the menu to prevent user from clicking on HUD
  this.$modalOverlayBackground = $('<div class="modalOverlayBehindMenu"></div>');
  gui.$onscreenGui_elem.append (this.$modalOverlayBackground);
};

/**
 * Initializes sliders (changing the mouse sensitivity, speed of avatar, ...)
 *
 * @private
 */
MainMenu.prototype.initSliders = function()
{
  var _this = this;

  /////////////////////
  // SPEED
  /////////////////////

  this.$movingSpeedSlider_elem = $("#movingSpeedSlider");

  this.$movingSpeedSlider_elem.slider({
    value: 50,
    min: 1,
    max: 100,
    change: function (event, ui)
    {

      // we want number in the range [0,1]
      Preferences.Control.MOVING_SPEED_RELATIVE = $(this).slider("value") / 100;
    }
  });

  this.$movingSpeedSlider_elem.siblings(".sliderRangeText").each (function(){
    $(this).appendTo (_this.$movingSpeedSlider_elem);
  });

  /////////////////////
  // MOUSE
  /////////////////////

  this.$mouseSensitivitySlider_elem = $("#mouseSensitivitySlider");

  this.$mouseSensitivitySlider_elem.slider({
    value: 50,
    min: 1,
    max: 100,
    change: function (event, ui)
    {
      // we want number in the range [0,1]

      Preferences.Control.MOUSE_RELATIVE_SENSITIVITY = $(this).slider("value") / 100;
    }
  });

  this.$mouseSensitivitySlider_elem.siblings(".sliderRangeText").each (function(){
    $(this).appendTo (_this.$mouseSensitivitySlider_elem);
  });

  /////////////////////
  // TIME (SUN)
  /////////////////////

  this._updateTimeOnTimeSlider = function()
  {
    var time = Util.timeInFloatToStandardTime (Preferences.Lights.TIME_OF_THE_DAY);
    _this.$timeSliderHandle_elem.text (time[0]+":"+time[1]);
  };

  this.$timeSlider_elem = $("#timeSlider");
  this.$timeSlider_elem.slider({
    value: 50,
    min: 0,
    max: 100,

    slide: function (event, ui)
    {

      // we need to access the value through ui, the elem.slider("value") has still old value in it
      Preferences.Lights.TIME_OF_THE_DAY = ui.value * 24 * 0.01; // convert 0-100 range to the 0-24 range, but also account for the fact that our maximum (i.e. 24h) shall be at 87%
      _this._updateTimeOnTimeSlider();

      VisualEffects.Sky.updateTimeSimulation();

      VisualEffects.Sky.updateSky();

      // (render the scene manually if the app cannot enter the main loop, e.g. because the menu is open)
      if (appMain.mainLoopSuspended)
      {
        renderer.render();
      }

    }
  });

  this.$timeSliderHandle_elem = this.$timeSlider_elem.find(".ui-slider-handle");
  this._updateTimeOnTimeSlider();

  this.$timeSlider_elem.siblings(".sliderRangeText").each (function(){
    $(this).appendTo (_this.$timeSlider_elem);
  });

  // sun speed
  this._updateTimeOnSpeedSlider = function()
  {
      var speed = parseInt(Preferences.Lights.TIME_SIMULATION_SPEED_INC);
      _this.$simulationSpeedSliderHandle_elem.text (speed + 'x');
  };

  this.$simulationSpeedSlider_elem = $("#simulationSpeedSlider");
  this.$simulationSpeedSlider_elem.slider({
    value: parseInt(Preferences.Lights.TIME_SIMULATION_SPEED_INC),
    min: 1,
    max: 1440,
    slide: function (event, ui)
    {
      Preferences.Lights.TIME_SIMULATION_SPEED_INC = ui.value;
      _this._updateTimeOnSpeedSlider();
    }
  });

  this.$simulationSpeedSliderHandle_elem = this.$simulationSpeedSlider_elem.find(".ui-slider-handle");
  this._updateTimeOnSpeedSlider();

  this.$simulationSpeedSlider_elem.siblings(".sliderRangeText").each (function(){
    $(this).appendTo (_this.$simulationSpeedSlider_elem);
  });

};

/**
 * Initializes (or reinitializes) select menu for changing the application resolution (in Display tab).
 *
 */
MainMenu.prototype.initSelectMenuForResolution = function() {
  /////////////////////////////////////////////////////////////////
  // INITIALIZING THE SELECTMENU FOR RESOLUTION
  /////////////////////////////////////////////////////////////////

  // first time calling this method
  if (!this.$option_resolution_select_elem) {
    this.$option_resolution_select_elem = $("#option_resolution_select");
    this.$option_resolution_select__option_elem = this.$option_resolution_select_elem.find("option");

    // parse the values 4, 2, 1, i.e. how much bigger/smaller the resolution options are
    this.resMultiplyCoeff = [];
    for (var i = 0; i < this.$option_resolution_select__option_elem.length; i++) {
      this.resMultiplyCoeff.push (parseFloat ($(this.$option_resolution_select__option_elem[i]).attr("value")));
    }

    var firstTimeCalling = true;
  }

  for (var i = 0; i < this.resMultiplyCoeff.length; i++) {
    $(this.$option_resolution_select__option_elem[i]).text (this.resMultiplyCoeff[i] + "x" + " (" + (gui.getDisplayWindow().windowWidth*this.resMultiplyCoeff[i]).toFixed(0) + "x" + (gui.getDisplayWindow().windowHeight*this.resMultiplyCoeff[i]).toFixed(0) + ")");
  }

  if (firstTimeCalling) {
		this.$option_resolution_select_elem.change(function () {
			var multiplyCoeff = $(this).val();
			console.log(multiplyCoeff);
			appMain.getRenderingWindow().windowDimensionScale = multiplyCoeff;
			appMain.getRenderingWindow().setWindowSize (gui.getDisplayWindow().windowWidth, gui.getDisplayWindow().windowHeight);
			picking.invalidatePickingMap();
			renderer.render();
		});
  }
};

/**
 * Initializes (or rather turns on) special behavior of light tab - they have no visible modal overlay and it is possible
 * to move with the window.
 *
 * @private
 */
MainMenu.prototype.initLightsTabBehavior = function() {
	var $lightOptionsTabContent = $("#tabContent_lights");
	var $lightOptionsActualContent = $lightOptionsTabContent.find("table");

	this.lightsTabCallbacks = {
		open: function() {
			$lightOptionsTabContent.css("height", "auto");
			this.$mainMenu_elem.tabs("option", "heightStyle", "content");
		}.bind(this),

		close: function() {
			this.$mainMenu_elem[0].setAttribute("style", null);
			$lightOptionsTabContent[0].setAttribute("style", null);
		}.bind(this)
	};
};

/**
 * Initializes the jQuery UI Tab widget, which is used as a basis for the menu.
 *
 * @private
 */
MainMenu.prototype._initTabWidget = function()
{
  var _this = this;

  /////////////////////////////////////////////////////////////////
  // INITIALIZING THE TAB WIDGET
  /////////////////////////////////////////////////////////////////

  this.$currentContentTab = null;

  this.$mainMenu_elem.tabs(
      {
        //heightStyle: "fill",
        active: 0, // set initial active tab

        beforeActivate: function (event, ui)
        {
          if (ui.oldTab.attr("tabContentName") === "tabContent_lights")
          {
            _this.lightsTabCallbacks.close();
          }

          if (ui.newTab.attr("tabContentName") === "tabContent_lights")
          {
            _this.lightsTabCallbacks.open();
          }
        },

        activate: function (event, ui)
        {
          // we need to update perfect scrollbar which is on the (content tab)
          _this.$currentContentTab = ui.newPanel;
        },

        create: function (event, ui)
        {
          // we need this to set initial value for currentContentTab
          _this.$currentContentTab = ui.panel;

          _this.initLightsTabBehavior();
        }
      });
};

/**
 * Transforms links which should take the user to the specific tab in the main menu.
 *
 * @param {jQuery} [element]                    element in which we will search for the links
 * @param {*}      [additionalClickCallback]    function executed after the link is clicked
 */
MainMenu.prototype.initTransformLinksToTabs = function (element, additionalClickCallback)
{
  var _this = this;

  if (!Util.isInitialized(element))
  {
    element = this.$mainMenu_elem;
  }

  /////////////////////////////////////////////////////////////////
  // TRANSFORMING LINKS TO THE TABS WITHIN THE TEXT
  /////////////////////////////////////////////////////////////////

  // (it seems that it is not possible to normally link to the tab via href, so we have to resort to the solution
  //  of registering click event and changing tab via JavaScript)

  // (note: must be done after initialization of the tab widget)

  element.find("a[href*=tabContent]:not('.ui-tabs-anchor')").each (function()
  {
    $(this).click (function(ev)
    {
      ev.preventDefault(); // kill normal link behavior

      _this.showMenu($(this).attr("href"));

      if (Util.isInitialized (additionalClickCallback))
      {
        additionalClickCallback();
      }
    });
  });
};

/**
 * Initializes buttons in the main menu (close cross button, language switch, continue button).
 *
 * @private
 */
MainMenu.prototype._initButtons = function()
{
  var _this = this;

  /////////////////////////////////////////////////////////////////
  // BUTTONS - REGISTERING EVENT CALLBACKS
  /////////////////////////////////////////////////////////////////

  Gui._initCloseCrossButton (this.$mainMenu_elem, function()
  {
    _this.hide();
  });
};

/**
 * Callback function executed when clicked on language switch button. Changes languages and translates entire GUI.
 *
 * @type {function}
 */
MainMenu.prototype.langswitchButtonCallback = function () {
	if (Preferences.Languages.getCurrentLanguage() === Preferences.Languages.EN) {
		Preferences.Languages.changeLanguageToCZ();
	} else {
		Preferences.Languages.changeLanguageToEN();
	}

	gui.translate();
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FETCHING CURRENT VALUES OF OPTIONS (RESOLUTION, SSAO, TIME OF THE DAY, ...)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * This method interlinks the UI with the application variables (registers event handlers on the UI elements).
 * Must be called, otherwise UI elements for changing options will not be working.
 *
 * @private
 */
MainMenu.prototype._initOptionLinkWithApplicationVariables = function ()
{
  var _this = this;

  this.optionToApplicationValueMap =
  {
    /////////////////////////
    // GENERAL TAB
    /////////////////////////

    option_mouse_axis_invert: {
      getVal: function()   {return Preferences.Control.INVERTED_MOUSE_Y;},
      setVal: function(val){       Preferences.Control.INVERTED_MOUSE_Y = val;},
      $radioOn: null,
      $radioOff: null
    },

    option_strafing: {
      getVal: function()   {return Preferences.Control.STRAFING;},
      setVal: function(val){       Preferences.Control.STRAFING = val;},
      $radioOn: null,
      $radioOff: null
    },

    option_collision_detection: {
      getVal: function()   {return Preferences.Collision.COLLISION_DETECTION;},
      setVal: function(val){       Preferences.Collision.COLLISION_DETECTION = val;},
      $radioOn: null,
      $radioOff: null
    },

    option_multiplayer: {
      getVal: function()   {return Preferences.Multiplayer.SHOW_USERS;},
      setVal: function(val){       Preferences.Multiplayer.SHOW_USERS = val;},
      $radioOn: null,
      $radioOff: null
    },

    /////////////////////////
    // DISPLAY TAB
    /////////////////////////

    option_ssao: {
      getVal: function()   {return Preferences.VisualEffects.POST_PROCESSING.SSAO;},
      setVal: function(val){       Preferences.VisualEffects.POST_PROCESSING.SSAO = val;},
      $radioOn: null,
      $radioOff: null
    },

    option_fxaa: {
      getVal: function()   {return Preferences.VisualEffects.POST_PROCESSING.FXAA;},
      setVal: function(val){       Preferences.VisualEffects.POST_PROCESSING.FXAA = val;},
      $radioOn: null,
      $radioOff: null
    },

    option_lens_flare: {
      getVal: function()   {return Preferences.VisualEffects.POST_PROCESSING.LENS_FLARE;},
      setVal: function(val){       Preferences.VisualEffects.POST_PROCESSING.LENS_FLARE = val;},
      $radioOn: null,
      $radioOff: null
    },

    option_shadows: {
      getVal: function()   {return Preferences.VisualEffects.SHADOWS;},
      setVal: function(val){       Preferences.VisualEffects.SHADOWS = val;},
      $radioOn: null,
      $radioOff: null
    },

    option_normal_bump_mapping: {
      getVal: function()   {return Preferences.VisualEffects.BUMP_AND_NORMAL_MAPS;},
      setVal: function(val){       Preferences.VisualEffects.BUMP_AND_NORMAL_MAPS = val;},
      $radioOn: null,
      $radioOff: null
    },

    option_show_fps: {
      getVal: function()   {return Preferences.Renderer.SHOW_FPS;},
      setVal: function(val){       Preferences.Renderer.SHOW_FPS = val;},
      $radioOn: null,
      $radioOff: null
    },

    /////////////////////////
    // LIGHTS TAB
    /////////////////////////

    option_time_simulation: {
      getVal: function()   {return Preferences.Lights.TIME_SIMULATION;},
      setVal: function(val){       Preferences.Lights.TIME_SIMULATION = val;},
      $radioOn: null,
      $radioOff: null
    },

    option_night_lights: {
      getVal: function()   {return Preferences.VisualEffects.NIGHT_LIGHTS;},
      setVal: function(val){       Preferences.VisualEffects.NIGHT_LIGHTS = val;},
      $radioOn: null,
      $radioOff: null
    },

    // flag variable, used when updating CSS in _fetchCurrentOptionValues()
    __executeCallback: true
  };

  this.optionToApplicationValueMap.__enumNames = Util.getEnumKeys (this.optionToApplicationValueMap);

  // for each value with defined map, register callback which change application variables upon radio buttons click
  for (var i = 0; i < this.optionToApplicationValueMap.__enumNames.length; i++)
  {
    var optionName = this.optionToApplicationValueMap.__enumNames[i];
    var option = this.optionToApplicationValueMap[ optionName ];

    // find radio buttons for turning on and off
    option.$radioOn = this.$mainMenu_elem.find("input[name=" + optionName + "][switchingon=true]");
    option.$radioOff = this.$mainMenu_elem.find("input[name=" + optionName + "][switchingon=false]");

		option.$radioOn.add(option.$radioOff).change(function (option) {
			var switchingOn = option.$radioOn.is(':checked') == true;
      if (_this.optionToApplicationValueMap.__executeCallback) {
        option.setVal (switchingOn);

				if (switchingOn) {
					option.$radioOn.prop('checked', true);
					option.$radioOff.prop('checked', false);
				} else {
					option.$radioOn.prop('checked', false);
					option.$radioOff.prop('checked', true);
				}

        if (appMain.mainLoopSuspended) {
          renderer.render();
        }
      }
    }.bind(null, option)); // JS note: we have to pass current value of option as an argument, otherwise it won't work
  }
};

/**
 * Retrieves current values of various application options (of those which can be changed from UI); those which are
 * turned on will have their radios checked, those who are not on will have them unchecked.
 *
 * @private
 */
MainMenu.prototype._fetchCurrentOptionValues = function ()
{
  //////////////////////////
  // RADIO BUTTON OPTIONS
  //////////////////////////

  this.optionToApplicationValueMap.__executeCallback = false; // we do not want to call callbacks now, we just want CSS to change
  for (var i = 0; i < this.optionToApplicationValueMap.__enumNames.length; i++)
  {
    var optionName = this.optionToApplicationValueMap.__enumNames[i];
    var option = this.optionToApplicationValueMap[ optionName ];

    // check if value in application variable is true
    if (option.getVal() == true) {
			option.$radioOn.prop('checked', true);
			option.$radioOff.prop('checked', false);
		} else {
			option.$radioOn.prop('checked', false);
			option.$radioOff.prop('checked', true);
		}
  }
  this.optionToApplicationValueMap.__executeCallback = true;

  //////////////////////////
  // SLIDER OPTIONS
  //////////////////////////

  // (we need to get from application variables [0,1] range into the [0,100] range for the jQuery UI)
  this.$movingSpeedSlider_elem.slider("value", Math.floor (Preferences.Control.MOVING_SPEED_RELATIVE * 100));
  this.$mouseSensitivitySlider_elem.slider("value", Math.floor (Preferences.Control.MOUSE_RELATIVE_SENSITIVITY * 100));

  this.$timeSlider_elem.slider("value", Math.floor (Preferences.Lights.TIME_OF_THE_DAY / (24 / 0.87) * 100)); // this is just reversed equation used in initSliders()
  this._updateTimeOnTimeSlider();
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// OPENING/CLOSING THE MENU
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Opens the menu.
 *
 * @param {string} [tabName]  name of the tab which should be visible when the menu appears (full tab name e.g. "#tabContent_general")
 */
MainMenu.prototype.showMenu = function (tabName)
{
  if (Util.isInitialized(tabName))
  {
    this.switchToTab (tabName);
  }

  // this will reflect any changes done to the application variables in the UI as well
  this._fetchCurrentOptionValues();

  gui.showModalBehind (true);

  this.visible = true;
	this.$mainMenu_elem.addClass('window-open');

  this.$mainMenu_elem.focus();
};

/**
 * Closes the main menu.
 */
MainMenu.prototype.hide = function()
{
	this.$mainMenu_elem.removeClass('window-open');

  gui.showModalBehind (false);

  this.visible = false;
};

// (interface for close cross buttons)
/**
 * Closes the main menu.
 *
 */
MainMenu.prototype.close = function ()
{
  this.hide();
};

/**
 * Switches menu to the desired tab.
 *
 * @param {string} tabName    name of the tab to be displayed (e.g. "#tabContent_general")
 */
MainMenu.prototype.switchToTab = function (tabName)
{
  this.$mainMenu_elem.tabs("option", "active", this._getTabIndex (tabName));
};

/**
 * Name of the tab (e.g. "#tabContent_general") for which we want to know its index (we can manipulate with the tab
 * through this index in the jQuery UI Tab widget).
 *
 * @param {string}    tabName     name of the tab to be displayed (e.g. "#tabContent_general")
 * @returns {number}              index of the tab in jQuery UI Tab widget
 * @private
 */
MainMenu.prototype._getTabIndex = function (tabName)
{
  return this.$mainMenu_elem.find('a[href=' + tabName +']').parent().index();
};

/**
 * Opens menu at the first tab.
 */
MainMenu.prototype.showDefault = function ()
{
	this.showMenu("#tabContent_general");
};

/**
 * Opens menu at the first tab.
 */
MainMenu.prototype.showLights = function ()
{
	this.showMenu("#tabContent_lights");
};
