"use strict";

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

/**
 * Handles everything user interface initialization, updating etc. Must be instantiated even before the main
 * application starts - we need to call various jQuery stuff for creating the menu, tooltips etc.
 *
 * @author smrceant (Antonin Smrcek)
 *
 * @constructor
 */
var Gui = function ()
{
  /**
   * Ratio between size of the rendering window (what was actually rendered by the hardware) and size of the display
   * window (what was shown on the screen; this could be typically twice as big as rendering window - then the ratio
   * would be 0.5). We use this ratio to convert mouse coordinates, which are in the domain of the display window,
   * to the domain of rendering window.
   *
   * @type {THREE.Vector2}
   */
  this.windowOriginalToRenderRatio = new THREE.Vector2();

  /**
   * Ratio between default display window size and current display window size.
   * It is needed for correct computation in minimap when the window is not in its original (default) size.
   *
   * @type {THREE.Vector2}
   */
  this.windowDisplayScaleRatio = new THREE.Vector2();

  /**
   * Reference to the minimap instance.
   *
   * @type {Minimap}
   */
  this.minimap = null;

  /**
   * Reference to the bigmap instance.
   *
   * @type {Bigmap}
   */
  this.bigmap = null;

  ////////////////////////////////////////////
  // HTML PAGE WINDOW DIMENSIONS
  ////////////////////////////////////////////

  /**
   * Represents size of the window used for displaying graphical content, i.e. content of CANVAS.
   * Size of this window is coupled with CANVAS CSS attributes "width" and "height". Do not mistake these
   * with CANVAS normal attributes "width" and "height", which specify WebGL viewport and how many pixels will be
   * actually rendered by hardware. The cool thing is, that if we render at smaller resolution than CSS attributes, e.g.
   * <canvas width="640" height="480" style="width: 1680px; height: 1050px">, then the image will still be displayed
   * at the 1680x1050, and filtering will be performed (so it will look blurry, but we didn't have to render so much
   * pixels... tradeoff quality-speed).
   *
   * The display window is (later) initialized to the maximum available window size so the application
   * looks "fullscreen" style.
   *
   * @type {Win}
   */
  this.windowDisplay = null;

  /**
   * Dialog for the viewpoint selection which appears when clicked on the on-screen viewpoint button.
   *
   * @type {WindowDialog}
   */
  this.viewpointDialog = null;

  /**
   * Dialog with application help which appears when clicked on the on-screen help button.
   *
   * @type {WindowDialog}
   */
  this.helpDialog = null;

  /**
   * Dialog with application help which appears when clicked on the on-screen tour button.
   *
   * @type {WindowDialog}
   */
  this.tourDialog = null;

  /**
   * Dialog with map of the virtual world which appears when clicked on the on-screen map button.
   *
   * @type {WindowDialog}
   */
  this.mapDialog = null;

  /**
   * SVG wrapper for the SVG map image.
   * Note: easiest option how to retrieve SVG image is to call mapImage.svg("get")._svg
   *
   * @type {jQuery}
   */
  this.mapImage = null;

  /**
   * If TRUE, picking captions will be displayed. This is default. If FALSE, no picking captions will be displayed
   * even if the mouse is over some pickable object (e.g. this is case when mouse is over some GUI button).
   *
   * @type {boolean}
   */
  this.allowPickCaptionDisplay = true;

  /**
   * Flag that says that we have just clickd on some GUI button. Used for preventing picking when in the current camera view,
   * the button is "over" some pickable object.
   *
   * @type {boolean}
   */
  this.justClickedGuiButton = false;

  // DO NOT CREATE ANYTHING THERE, WE MUST WAIT FOR STUFF TO BE DONE. THE INIT() WILL BE CALLED WHEN THE TIME IS RIGHT.
};

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

/**
 * Initializes main menu - dynamically loads its HTML and inserts it into the page.
 *
 * @private
 * @param {function} additionalLoadCallback    function executed at the end of the success() method, i.e. when the main menu is loaded and created
 */
Gui.prototype.initMainMenu = function (additionalLoadCallback)
{
  var _this = this;

  // load main menu
  $.ajax(
      {
        url: Preferences.Gui.MAIN_MENU_HTML,
        dataType: 'html',

        success: function (data)
        {
          // append HTML to the document
          $('body').append(data);

          _this.mainMenu = new MainMenu();

          if (Util.isInitialized(additionalLoadCallback))
          {
            additionalLoadCallback();
          }
        },

        // this is needed to fix the error "junk after document"
        beforeSend: function (xhr)
        {
          if (xhr.overrideMimeType)
          {
            xhr.overrideMimeType("text/html");
          }
        }
      });
};

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

/**
 * Initializes the entire GUI.
 *
 * @param {AssetJob|Asset} guiLoading   job which waits for the entire GUI to be loaded
 */
Gui.prototype.init  = function (guiLoading)
{
  var _this = this;

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

  this.$onscreenGui_elem = $('<div id="onscreenGui"></div>');
  $("body").append (this.$onscreenGui_elem);

  ///////////////////////////////////////////////////////////////////////
  // HUD INITIALIZATION
  ///////////////////////////////////////////////////////////////////////

  this.initOnscreenButtons();

  ///////////////////////////////////////////////////////////////////////
  // WINDOWS (BUT NOT IN GUI!)
  ///////////////////////////////////////////////////////////////////////

  this.windowDisplayDefault = new Win ($(canvas).width(), $(canvas).height());

  // init display window to the maximum available window size (maximum size without scrollbars. fuck scrollbars)
  this.windowDisplay = new Win ($(canvas).width(), $(canvas).height());
  this.windowDisplay.addResizingCallback (function ()
      {
        if (Util.isInitialized (appMain))
        {
          appMain.getRenderingWindow().setWindowSize (_this.windowDisplay.windowWidth, _this.windowDisplay.windowHeight);
        }

        if (Util.isInitialized (appMain))
        {
          Win.computeRatioToAnotherWindow(appMain.getRenderingWindow(), _this.windowDisplay, _this.windowOriginalToRenderRatio);
        }

        Win.computeRatioToAnotherWindow (_this.windowDisplayDefault, _this.windowDisplay, _this.windowDisplayScaleRatio);

        renderer.resize (_this.windowDisplay.windowWidth, _this.windowDisplay.windowHeight);
      }
  );
  this.windowDisplay.addResizingCallback (function()
  {
    if (_this.mainMenu)
    {
      _this.mainMenu.initSelectMenuForResolution();
    }
  });

  this.windowDisplay.setWindowSize(); // updates window size and also the callback which we have just added will be executed (well both will be executed since we have added two callbacks, but we need especially the first one)

  ///////////////////////////////////////////////////////////////////////
  // MINIMAP
  ///////////////////////////////////////////////////////////////////////

  this.minimap = new Minimap (this.$onscreenGui_elem, guiLoading); // must be after window definitions
	this.minimap.$minimapTwin_elem.click(function () {
		if (_this.mapDialog) {
			_this.mapDialog.openDialog();
		}
	});

  ///////////////////////////////////////////////////////////////////////
  // CONTENT OBJECT (TEXT AND STUFF)
  ///////////////////////////////////////////////////////////////////////

  this.contentObject = new ContentObject (this.$onscreenGui_elem);

  ///////////////////////////////////////////////////////////////////////
  // MAIN MENU
  ///////////////////////////////////////////////////////////////////////
  /**
   *
   * @type {MainMenu}   handler to the main menu object
   */
  this.mainMenu = null;
  this.initMainMenu (function()
  {
    // this will be initialized after the main menu is initialized
    _this.createViewpointDialog (Preferences.Viewpoint.VIEWPOINTS_IN_DIALOG_LIST);
    _this.createHelpDialog();
    _this.createTourDialog();
    _this.createMapDialog();
  });

  // compute positions of viewpoint icons in the map
  Viewpoint.computePositions2dInMap (_this.getUnderlyingMap());

  ///////////////////////////////////////////////////////////////////////
  // PICKABLE OBJECTS
  ///////////////////////////////////////////////////////////////////////

  /**
   * Container used for displaying picking text near the objects.
   *
   * @type {jQuery}
   */
  this.$pickableObjectUnderMouse = $('<div id="pickableObjectUnderMouse" class="unselectable"></div>');
  this.$onscreenGui_elem.append (this.$pickableObjectUnderMouse);

  // assign same mouse callbacks as for canvas, so we won't stop interacting with the app when
  // the mouse is over the <div> with pick caption
  if (Util.isInitialized (control))
  {
    control.registerApplicationMouseListeners (this.$pickableObjectUnderMouse[0]);
  }

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

  this.contentObject.translate();
};

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

/**
 * Called when the tour window is resized.
 *
 */
Gui.prototype.resizeCallback = function()
{
  var _this = this;

  // the size of the window which is used for displaying graphic content has changed
  // (we delay it a little - this should fix bug that the resize event is fired before the window changed when it is maximized)
  setTimeout (function()
  {
    _this.windowDisplay.setWindowSize (Preferences.Window.detectDimensionWithoutScrollbars());
    _this.minimap._resizeCallback();
  }, 150);
  // (do not write after this)
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DYNAMIC CREATION OF BUTTONS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Searches through given element for <div identificationAttribute="true"> elements, where identificationAttribute is
 * the name of the attribute (e.g. "closeCrossButton"), which signalize that we should insert a button at that place.
 *
 * @private
 * @param {jQuery}    element                   in which element we will be searching for the button
 * @param {string}    identificationAttribute   our target HTML elements have this attribute in their markup
 * @param {function}  createButtonFunction      the function used for creating the button
 * @param {function}  onclickCallback           onclick callback which will be registered to the button
 */
Gui._initButton = function (element, identificationAttribute, createButtonFunction, onclickCallback)
{
  element.find("["+identificationAttribute+"]").each (function()
  {
    Gui._replaceElementWithButton ($(this), createButtonFunction, onclickCallback);
  });
};

/**
 * Replaces element with the button created via given create function.
 *
 * @private
 * @param {jQuery}    replacedElement       which element will be deleted and close cross button will be placed instead of it
 * @param {function}  createButtonFunction  function used for creating the button
 * @param {function}  onclickCallback       onclick callback which will be registered to close button
 */
Gui._replaceElementWithButton = function (replacedElement, createButtonFunction, onclickCallback)
{
  // create the button
  var $button = createButtonFunction (onclickCallback);

  // add CSS classes from dummy element (sometimes we define them) to our button
  // (note: they are stored in the custom_button_class attribute rather than standard class attribute, because
  //        class attribute of the element very often obtains some jQuery UI classes which we DO NOT want to transfer
  //        tou our new button)
  $button.addClass (replacedElement.attr("custom_button_class"));

  // replace the element
  replacedElement.replaceWith ($button);
};

//////////////////////////////////////////////////////////////
// CLOSE CROSS BUTTON
//////////////////////////////////////////////////////////////

/**
 * Searches through given element for close button.
 *
 * @private
 * @param {jQuery}    element           in which element we will be searching for a close button
 * @param {function}  onclickCallback   onclick callback which will be registered to found close button
 */
Gui._initCloseCrossButton = function (element, onclickCallback)
{
  Gui._initButton (element, "crossCloseButton", Gui._createCloseCrossButton, onclickCallback);
};

/**
 * Replaces element with a close button.
 *
 * @private
 * @param {jQuery}    replacedElement   which element will be deleted and close cross button will be placed instead of it
 * @param {function}  onclickCallback   onclick callback which will be registered to close button
 */
Gui.replaceElementWithCloseCrossButton = function (replacedElement, onclickCallback)
{
  Gui._replaceElementWithButton (replacedElement, Gui._createCloseCrossButton, onclickCallback);
};

/**
 * Creates a classical close button with cross image.
 *
 * @private
 * @param {function}  onclickCallback   callback executed when the button is clicked
 */
Gui._createCloseCrossButton = function (onclickCallback)
{
	var $closeCrossButton = $(
		'<div class="guiClose">'+
		'  <button langcaption_title="RETURN_TO_VIRTUAL_WALK" class="guiCloseButton ui-button ui-widget ui-state-default" type="button" role="button">'+
		'    <img src="data/gui/icons/close.png" class="guiCloseButtonIcon" langcaption_title="RETURN_TO_VIRTUAL_WALK" />'+
		'  </button>'+
		'</div>'
	);

  $closeCrossButton.click (onclickCallback);

  return $closeCrossButton;
};

//////////////////////////////////////////////////////////////
// STANDARD GUI BUTTON
//////////////////////////////////////////////////////////////

/**
 * Turns a <button> element from the DOM into the jQuery UI button, or rather into something called standard GUI button, which
 * has CSS hover style and click callback registered. With this, the button definition in the HTML can be short
 * and without redundant things like classes, roles etc. which are same for all GUI buttons.
 *
 * Note: if you want to have translated text in the button, and you want it properly styled, it is necessary to put the <span> with the text
 * inside the <button> like this: <button><span langcaption="YOUR_CONTENT_STRING_IDENTIFICATOR"></span></button>
 *
 * @param {jQuery}    buttonElement     <button> element which will be turned into standard UI button
 * @param {funcion}   onclickCallback   callback executed when the button is clicked
 */
Gui.makeIntoStandardButton = function (buttonElement, onclickCallback)
{
  // add correct attributes and classes to the button
  buttonElement
      .attr("role", "button")
      .attr("type", "button")
      .addClass("ui-button ui-widget ui-state-default ui-button-text-only menu_button");

  // (improvement: better would be to do mouse events via CSS)
  // register CSS for hovering and click callback
  buttonElement
    .on("mouseover", function()
    {
      $(this).addClass("menu_buttonHover");
      $(this).removeClass("menu_button");
    })
    .on("mouseout", function()
    {
      $(this).removeClass("menu_buttonHover");
      $(this).addClass("menu_button");
    })
    .on("click", onclickCallback);

  // add correct class to the button text
  var $buttonText = $(buttonElement.find("span")[0]);
  $buttonText.addClass("ui-button-text");
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ON-SCREEN BUTTONS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Enums for types of onscreen GUI buttons (note: the button in this context is NOT meant as <button>, but merely as clickable screen element).
 * There are 2 types: normal buttons (e.g. Menu, Help) and virtual keyboard buttons (arrows up, down, ...).
 * The enum contains 2 keys, and as a value, each contains concatenated list of CSS classes which will be applied to the button (separated by spaces).
 * elementContainerClasses is list of classes which will be applied to the whole button <div> element.
 * imageContainerClasses is list of classes applied to the <div> element surrounding button <img> element (see createOnscreenButton()).
 */
Gui.OnscreenButtonTypes = {};

Gui.OnscreenButtonTypes.NORMAL_BUTTON =
  {
    elementContainerClasses: "gui-round-button",
    imageContainerClasses:   "gui-round-button-circle",
    onscreenButtonClasses:   "",
    createButtonCaption: true
  };

Gui.OnscreenButtonTypes.NORMAL_BUTTON_HELP_SECTION =
  {
    elementContainerClasses: "inlineBlockElement " + Gui.OnscreenButtonTypes.NORMAL_BUTTON.elementContainerClasses,
    imageContainerClasses:   "noHover " + Gui.OnscreenButtonTypes.NORMAL_BUTTON.imageContainerClasses,
    onscreenButtonClasses:   "",
    createButtonCaption: false,
    generateWithoutId: true
  };

Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON =
  {
    elementContainerClasses: "gui-keyboard-button unselectable",
    imageContainerClasses:   "gui-keyboard-button-rectangle",
    onscreenButtonClasses:   "onscreenButtonCaptionVirtualKeyboard",
    createButtonCaption: false
  };

Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON_HELP_SECTION =
{
  elementContainerClasses: "inlineBlockElement " + Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON.elementContainerClasses,
  imageContainerClasses:   "noHover " + Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON.imageContainerClasses,
  onscreenButtonClasses:   "",
  createButtonCaption: false,
  generateWithoutId: true
};

/**
 * Creates a button which is part of the always visible, on-screen GUI.
 *
 * @param   {string} name           identificator of the button (e.g. "help")
 * @param   {jQuery} parentElement  into which HTML element should be newly created button appended
 * @param   {Object} buttonOptions  additional options for button, e.g. if we will create caption for it
 * @returns {jQuery}                newly created button
 */
Gui.prototype.createOnscreenButton = function (name, parentElement, buttonOptions)
{
  var attrId = Util.isInitialized (buttonOptions.generateWithoutId) ? '' : ' id="buttonOnscreen'+Util.makeFirstLetterCapital(name)+'"';

	var buttonElement = $(
		'<div'+attrId+' class="unselectable onscreenGui onscreenGuiElement buttonOnscreen'+Util.makeFirstLetterCapital(name)+' '+buttonOptions.elementContainerClasses+'" ondragstart="return false;">'+
		'  <div class="'+buttonOptions.imageContainerClasses+'">'+
		'    <img class="buttonOnscreenImage'+Util.makeFirstLetterCapital(name)+'" src="data/gui/icons/'+name+'.png" />'+
		'  </div>'+
		'</div>'
	);

  this.disablePickingCaptionWhenMouseOver (buttonElement);
  this.stopMouseClickEvents (buttonElement);

  if (buttonOptions.createButtonCaption)
  {
	var buttonCaptionElement = $(
		'<div class="onscreenButtonCaption fontOutline'+' '+buttonOptions.onscreenButtonClasses+'" langcaption="GUI_ONSCREEN_'+name.toUpperCase()+'"></div>'
	);

    buttonElement.append (buttonCaptionElement);
  }

  parentElement.append (buttonElement);

  return buttonElement;
};

/**
 * Creates all on-screen buttons of the GUI (Menu, Help, Virtual Keyboard etc.)
 */
Gui.prototype.initOnscreenButtons = function()
{
  var _this = this;

  ///////////////////////////////////////////////////////////
  // NORMAL BUTTONS
  ///////////////////////////////////////////////////////////

	// initialize clock
	Clock(this.$onscreenGui_elem);

	this.$buttonOnscreenMenu_elem = this.createOnscreenButton("menu", this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
	this.$buttonOnscreenMenu_elem.addClass('right');
  this.$buttonOnscreenMenu_elem.mousedown (function(ev) // note: we need mousedown event instead of click so we fire it faster than e.g. mouse up events is performed, which would mess with our picking in some situations
  {
    _this.justClickedGuiButton = true;

    _this.mainMenu.showDefault();
  });

  this.$buttonOnscreenHelp_elem = this.createOnscreenButton("help", this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
	this.$buttonOnscreenHelp_elem.addClass('right');
  this.$buttonOnscreenHelp_elem.mousedown (function(ev)
  {
    _this.justClickedGuiButton = true;

    _this.helpDialog.openDialog();
  });

	this.$buttonOnscreenGui_elem = this.createOnscreenButton("gui", this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
	this.$buttonOnscreenGui_elem.addClass('right');
	_this.$onscreenGui_elem.addClass('show-gui');
  this.$buttonOnscreenGui_elem.mousedown (function(ev) // note: we need mousedown event instead of click so we fire it faster than e.g. mouse up events is performed, which would mess with our picking in some situations
  {
		_this.justClickedGuiButton = true;

		_this.$onscreenGui_elem.toggleClass('hide-gui');
		_this.$onscreenGui_elem.toggleClass('show-gui');
  });

  // tour dialog
  this.$buttonOnscreenTour_elem = this.createOnscreenButton("tour", this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
  this.$buttonOnscreenTour_elem.mousedown (function(ev)
  {
    _this.justClickedGuiButton = true;

    _this.tourDialog.openDialog();
  });

  // tour pause
  this.$buttonOnscreenTour_elem = this.createOnscreenButton("tour_pause", this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
  this.$buttonOnscreenTour_elem.mousedown (function(ev)
  {
    _this.justClickedGuiButton = true;
  });

	///////////////////////////////////////////////////////////
  // WALK/FLY BUTTONS
  ///////////////////////////////////////////////////////////

  this.$buttonOnscreenWalk_elem  = this.createOnscreenButton("walk", this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
  this.$buttonOnscreenFly_elem   = this.createOnscreenButton("fly",  this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);

  /**
   * Displays one button and hides the another (from the walk and fly on-screen buttons), based on the player mode.
   *
   * @param {boolean} walking   whether is player currently in WALK mode or not
   */
  var displayWalkOrFlyButton = function (walking)
  {
    if (walking)
    {
      _this.$buttonOnscreenFly_elem.show();
      _this.$buttonOnscreenWalk_elem.hide();

      _this.$virtualKeyboardDownFly_elem.hide();
      _this.$virtualKeyboardUpFly_elem.hide();
    }
    else
    {
      _this.$buttonOnscreenWalk_elem.show();
      _this.$buttonOnscreenFly_elem.hide();

      _this.$virtualKeyboardDownFly_elem.show();
      _this.$virtualKeyboardUpFly_elem.show();
    }
  };

  /**
   * Switches player mode and displays correct button in the GUI.
   */
  this.switchWalkFly = function(flyMode)
  {
		if(flyMode){
			player.turnFlyMode();
		}
		else{
			player.switchWalkFlyMode();
		}

    displayWalkOrFlyButton (player.inWalkMode());
  };

  $([this.$buttonOnscreenWalk_elem[0], this.$buttonOnscreenFly_elem[0]]).mousedown (function(ev)
  {
    _this.justClickedGuiButton = true;

    _this.switchWalkFly();

    ev.stopPropagation();
  });

	///////////////////////////////////////////////////////////
  // Language buttons
  ///////////////////////////////////////////////////////////

  this.$buttonOnscreenCS_elem = this.createOnscreenButton('cs', this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
	this.$buttonOnscreenCS_elem.addClass('right');
  this.$buttonOnscreenEN_elem = this.createOnscreenButton('en', this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
	this.$buttonOnscreenEN_elem.addClass('right');

  /**
   * Displays one button and hides the another (from the walk and fly on-screen buttons), based on the player mode.
   *
   * @param {boolean} walking   whether is player currently in WALK mode or not
   */
	var displayLanguage = function () {
		if (Preferences.Languages.getCurrentLanguage() === Preferences.Languages.EN) {
			_this.$buttonOnscreenCS_elem.show();
			_this.$buttonOnscreenEN_elem.hide();
		} else {
			_this.$buttonOnscreenEN_elem.show();
			_this.$buttonOnscreenCS_elem.hide();
		}
	};

  /**
   * Switches player mode and displays correct button in the GUI.
   */
  this.switchLanguage = function()
  {
		MainMenu.prototype.langswitchButtonCallback();
    displayLanguage ();
  };

  $([this.$buttonOnscreenCS_elem[0], this.$buttonOnscreenEN_elem[0]]).mousedown (function(ev)
  {
    _this.justClickedGuiButton = true;
    _this.switchLanguage();
    ev.stopPropagation();
  });

  ///////////////////////////////////////////////////////////
  // NORMAL BUTTONS
  ///////////////////////////////////////////////////////////

	this.$buttonOnscreenFullscreen_elem = this.createOnscreenButton("fullscreen", this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
	this.$buttonOnscreenFullscreen_elem.addClass('right');
  this.$buttonOnscreenFullscreen_elem.mousedown (function(ev)
  {
    _this.justClickedGuiButton = true;

    Fullscreen.Switch();
  });

  this.$buttonOnscreenMap_elem        = this.createOnscreenButton("map",       this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
  this.$buttonOnscreenMap_elem.mousedown (function(ev)
  {
    _this.justClickedGuiButton = true;

    _this.mapDialog.openDialog();
  });

  this.$buttonOnscreenViewpoint_elem  = this.createOnscreenButton("viewpoint", this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
  this.$buttonOnscreenViewpoint_elem.mousedown (function(ev)
  {
    _this.justClickedGuiButton = true;

    _this.viewpointDialog.openDialog();
  });

  this.$buttonOnscreenObjects_elem  = this.createOnscreenButton("objects", this.$onscreenGui_elem, Gui.OnscreenButtonTypes.NORMAL_BUTTON);
  this.$buttonOnscreenObjects_elem.mousedown (function(ev)
  {
    _this.justClickedGuiButton = true;

    Preferences.VisualEffects.POST_PROCESSING.PICKING_VISUALIZATION = !Preferences.VisualEffects.POST_PROCESSING.PICKING_VISUALIZATION;

    // find captions and translate them, use content string with suffix appended
    var $captions = _this.$buttonOnscreenObjects_elem.find("[langcaption]");
    $captions.each(function()
    {
			var suffix = typeof contentStringTranslationSuffix !== 'undefined' ? contentStringTranslationSuffix : '';
      $(this).html (ContentLibrary.getContentString ($(this).attr("langcaption") + suffix) );
    });
  });

  ///////////////////////////////////////////////////////////
  // VIRTUAL KEYBOARD
  ///////////////////////////////////////////////////////////

  this.$virtualKeyboardLeft_elem  = this.createOnscreenButton ("left",  this.$onscreenGui_elem, Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON);
  this.$virtualKeyboardDown_elem  = this.createOnscreenButton ("down",  this.$onscreenGui_elem, Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON);
  this.$virtualKeyboardUp_elem    = this.createOnscreenButton ("up",    this.$onscreenGui_elem, Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON);
  this.$virtualKeyboardRight_elem = this.createOnscreenButton ("right", this.$onscreenGui_elem, Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON);

  this.$virtualKeyboardDownFly_elem  = this.createOnscreenButton ("downfly",  this.$onscreenGui_elem, Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON);

  // instead of creating a deep copy of Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON using e.g. var copy = jQuery.extend(true, {}, Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON);
  // and then passing that copy, we will change the property directly in the object - this is not a good practice, but we know that this button is the last virtual keyboard button used, so
  // we won't mess up anything by direct overwrite
  Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON.createButtonCaption = true;
  this.$virtualKeyboardUpFly_elem    = this.createOnscreenButton ("upfly",    this.$onscreenGui_elem, Gui.OnscreenButtonTypes.VIRTUAL_KEYBOARD_BUTTON);

  ///////////////////////////////////////////////////////////////////////
  // VIRTUAL KEYBOARD EVENT REGISTRATION
  ///////////////////////////////////////////////////////////////////////

  Gui.setMouseListenerForKeyState (this.$virtualKeyboardUp_elem[0],       "MOVE_FORWARD");
  Gui.setMouseListenerForKeyState (this.$virtualKeyboardRight_elem[0],    "LOOK_RIGHT");
  Gui.setMouseListenerForKeyState (this.$virtualKeyboardDown_elem[0],     "MOVE_BACK");
  Gui.setMouseListenerForKeyState (this.$virtualKeyboardLeft_elem[0],     "LOOK_LEFT");

  Gui.setMouseListenerForKeyState (this.$virtualKeyboardUpFly_elem[0],    "FLY_UP");
  Gui.setMouseListenerForKeyState (this.$virtualKeyboardDownFly_elem[0],  "FLY_DOWN");

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

  // display correct button with switches
	displayLanguage ();
  displayWalkOrFlyButton (player.inWalkMode());
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MISC
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Disables displaying of picking caption when mouse is over the given element. Used on all GUI elements.
 *
 * @param {jQuery} element    GUI element, e.g. button
 */
Gui.prototype.disablePickingCaptionWhenMouseOver = function (element)
{
  element.mouseover (function()
  {
    // forbid pick caption displaying; otherwise, when some pick caption is visible, it would be still visible and that will look bad
    gui.allowPickCaptionDisplay = false;
  });

  element.mouseout (function()
  {
    gui.allowPickCaptionDisplay = true;
  });
};

/**
 * Forbids mouse down event to continue bubbling further. This prevents inadvertently controlling the application
 * when mouse is interacting with GUI.
 *
 * Note: it is better to leave mouse up event to continue. If user holds mouse and hovers on the e.g. onscreen keyboard
 * and then releases the mouse on it, it will work ok. But if the mouse up event would be stopped, it would not work.
 *
 * @param {jQuery} element    GUI element, e.g. button
 */
Gui.prototype.stopMouseClickEvents = function (element)
{
  element.mousedown(function(event)
  {
    event.stopPropagation();
  });
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CANVASES (WEBGL, 2D CONTEXT)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Gui.prototype.initCanvases = function()
{
  // (note: we are assigning canvas elements to the variables, not canvas containers, that's why we grab [0])
  canvas   = $(this.createCanvas ("webgl"))[0];
  canvas2d = $(this.createCanvas ("canvas2d"))[0];
};

/**
 * Creates a new CANVAS element.
 *
 * @param   {string}  name    id of the new CANVAS
 * @returns {jQuery}          CANVAS element as jQuery object
 */
Gui.prototype.createCanvas = function (name)
{
  var canvasElement = $(
          '<canvas id="'+name+'">'+
          '  <span langcaption="CANVAS_NOT_SUPPORTED"></span>'+
          '</canvas>'
  );

  $("body").append (canvasElement);

  canvasElement.hide();

  // setting width and height ATTRIBUTES of the CANVAS - these are not CSS attributes
  // this is not necessary, since this will be set by renderer.init()
//  canvasElement.attr("width", canvasElement.width());
//  canvasElement.attr("height", canvasElement.height());

  return canvasElement;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DIALOG CREATION
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Creats the Viewpoint window.
 *
 * @param {Viewpoint[]} viewpoints    which viewpoints will be displayed in the window
 */
Gui.prototype.createViewpointDialog = function (viewpoints)
{
  /**
   * Creates a container with snapshot image and caption below. Used in viewpoint dialog and in map hover windows.

   * @param   {Viewpoint} viewpoint
   * @returns {jQuery}                    container with image and caption
   */
  var createViewpointItem = function (viewpoint)
  {
    var $viewpointItem = $(
			'<div class="viewpointContainer">'+
						'<div class="image">'+
            '  <img src="'+viewpoint.getSnapshotImageFilename()+'" class="viewpointSnapshot" />'+
						'</div>' +
            '  <p langcaption="'+viewpoint.getCaption()+'"></p>'+
            '</div>'
    );

    $viewpointItem.click (function()
    {
      viewpoint.setAsCurrentViewpoint();

      viewpointDialog.close();
    });

    return $viewpointItem;
  };

  var $viewpointListGrid = $(
      '<div class="viewpointListGrid window-scroll unselectable" ondragstart="return false">' +
      '</div>');

  for (var i = 0; i < viewpoints.length; i++)
  {
    $viewpointListGrid.append ( createViewpointItem (viewpoints[i]) );
  }

  // create instance of the window dialog, do not pass ContentObject - we can't get a pointer to the new dialog when we are creating it... :-(
  var viewpointDialog = new WindowDialog ("viewpointList", "GUI_VIEWPOINT_SELECT_DIALOG_TITLE", null, null, "viewpointListDialog", false, null, $viewpointListGrid);

  // call init() of the created window dialog, pass ContentObject which now have access to the new dialog pointer
  viewpointDialog.init
  (
      new ContentObject (viewpointDialog.getDialogWrapper())
  );

  this.viewpointDialog = viewpointDialog;
};

/**
 * The the window with Help.
 */
Gui.prototype.createHelpDialog = function()
{
  var _this = this;

  // create instance of the window dialog, do not pass ContentObject - we can't get a pointer to the new dialog when we are creating it... :-(
  var helpDialog = new WindowDialog (
		'helpDialog',
		null,
		null,
		null,
		'helpDialog',
		false,
		function (dialog, dialogInitJob){},
		'gui/help.html');

  // call init() of the created window dialog, pass ContentObject which now have access to the new dialog pointer
  helpDialog.init
  (
      new ContentObject (helpDialog.getDialogWrapper())
  );

  this.helpDialog = helpDialog;
};

/**
 * The window with tour.
 */
Gui.prototype.createTourDialog = function()
{
  var _this = this;

  // create instance of the window dialog, do not pass ContentObject - we can't get a pointer to the new dialog when we are creating it... :-(
  var tourDialog = new WindowDialog ("tourDialog", null, null, null, "viewpointListDialog",	false, function (dialog, dialogInitJob)
	{
		var $dialogContent = dialog.getDialog();
		$.post('./data/tour/tour.json', function(response){
			Tour.Init(response, $dialogContent);
		});

		// hide overlay after dialog close
		$dialogContent.on('dialogclose', function(){
			$('.modalOverlayBehindMenu').hide();
		});
	}, "gui/tour.html");

  // call init() of the created window dialog, pass ContentObject which now have access to the new dialog pointer
  tourDialog.init
  (
    new ContentObject (tourDialog.getDialogWrapper())
  );

  this.tourDialog = tourDialog;
};

Gui.prototype.createMapDialog = function () {
	var _this = this;

  var mapDialog = new WindowDialog ('mapDialog', null, null, null, 'mapDialog',	false, function (dialog, dialogInitJob) {
		var $dialogContent = dialog.getDialog();
		$.post('./data/tour/tour.json', function(response) {

			var svg = $dialogContent.find('svg#map-map');

			var str = '';
			for (var name in response.texts) {
				var vertex = response.texts[name];
				var x = response.vertices[vertex * 3];
				var y = -response.vertices[vertex * 3 + 1];
				var z = response.vertices[vertex * 3 + 2];
				str += '<circle data-cz="' + z + '" cx="' + x + '" cy="' + y + '" r="2.5"/>';
				str += '<text x="' + (x + 5) + '" y="' + (y + 3) + '">' + name + '</text>';
			}

			svg.html(str);

			svg.click(function (event) {
				var target = $(event.target);

				if (target.is('circle')) {
					var position = {
						x: parseFloat(target.attr('cx')),
						y: parseFloat(target.attr('data-cz')) + 3,
						z: parseFloat(target.attr('cy')),
					}

					appMain.getCurrentEntity().camera.setPosition(position, true); // cammera position set
					dialog.close(); // close this dialog
				}
			});
		});

		// hide overlay after dialog close
		$dialogContent.on('dialogclose', function() {
			$('.modalOverlayBehindMenu').hide();
		});
	}, "gui/map.html");

  mapDialog.init(new ContentObject (mapDialog.getDialogWrapper()));

  this.mapDialog = mapDialog;
};

/**
 * SVG element with the map. Can be traversed by jQuery.
 *
 * @returns {jQuery}
 */
Gui.prototype.getMapImage = function ()
{
  return this.mapImage;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TRANSLATION
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Translates ALL text which can be read in the GUI (either on-screen or in some menu) into the current application
 * language.
 */
Gui.prototype.translate = function()
{
  this.mainMenu.contentObject.translate();

  this.contentObject.translate(); // translate on-screen GUI

  this.viewpointDialog.contentObject.translate();
  this.helpDialog.contentObject.translate();
  this.tourDialog.contentObject.translate();
  this.mapDialog.contentObject.translate();

  this.minimap.updateAreaCaption();
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// OTHER
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Registers mouse down/up event listeners on given element such that when mouse is down -> state flag variable is set to true
 * and when mouse is up or moves out of the element -> state variable is false.
 * Used by virtual keyboard (examples of the state flag: "moveForward", "moveBack", etc.).
 *
 * @param {HTMLElement} element           HTML element
 * @param {string}      stateFlag
 */
Gui.setMouseListenerForKeyState = function (element, stateFlag)
{
  element.onmousedown = function (ev)
  {
    control.actionsState[stateFlag] = true; // use string to access state flag at runtime (we cant just pass e.g. state.moveForward as a pointer, it would not work, cause it would evaluate to false or null immediately...)
  };

  element.onmouseup = element.onmouseout = function (ev)
  {
    // we dont have to mess with the gui.justClickedGuiButton to prevent picking info dialogs from opening, dunno why it is sufficient just stop propagation (it does not work on other buttons...)
    ev.stopPropagation();
    ev.stopImmediatePropagation();

    control.actionsState[stateFlag] = false;
  };
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MINIMAP UPDATE OF PLAYER POSITION
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Updates position of the marker, vision cone and other things in minimap.
 *
 * @param   {Entity}  entity
 */
Gui.prototype.updateMinimap = function (entity)
{
  this.minimap.updateMinimap (entity);
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PICKING (OBJECTS OF INTEREST INFO)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Shows tourist guide information for the object.
 *
 * @param {string}  pickableObject    internal name of the picked object (e.g. "courtyard1")
 */
Gui.prototype.showPickableObjectInfo = function (pickableObject)
{
  ObjectInfo.openDialog (pickableObject);
};

/**
 * Shows name of the pickable model which is currently under the mouse (caption will be showed near the cursor).
 *
 * @param {?string} pickedObject
 */
Gui.prototype.showPickableModelNameUnderMouse = function (pickedObject)
{
    if (pickedObject)
    {
      this._showPickCaption (ContentLibrary.getContentString(pickedObject));
    }
    // no pickable model under mouse
    else
    {
      this.hidePickableModelName();
    }

  if (!this.allowPickCaptionDisplay)
  {
    this.hidePickableModelName();
  }
};

/**
 * Updates text content of the pick caption and positions it.
 *
 * @param   {string}    info   which text will be written into the pick caption
 * @private
 */
Gui.prototype._showPickCaption = function (info)
{
  Gui.setElementPosition(this.$pickableObjectUnderMouse[0], control.mouse.x + Preferences.Picking.PICKING_CAPTION_OFFSET_FROM_MOUSE.x, control.mouse.y + Preferences.Picking.PICKING_CAPTION_OFFSET_FROM_MOUSE.y);
  this.$pickableObjectUnderMouse[0].innerHTML = info;
  this.$pickableObjectUnderMouse[0].style.display = "block";

  this.ensureElementIsWithinScreen (this.$pickableObjectUnderMouse[0]);
};

/**
 * Makes sure that the element fits into the window. Used for preventing the creation of scrollbars when the pick caption is near the window edges.
 *
 * @param {HTMLElement} element
 */
Gui.prototype.ensureElementIsWithinScreen = function (element)
{
  this._tmp_widthDiff = element.getBoundingClientRect().right - $(window).width();
  if (this._tmp_widthDiff > 0)
  {
    Gui.setElementPosition(element, element.getBoundingClientRect().left - this._tmp_widthDiff, element.getBoundingClientRect().top);
  }

  this._tmp_heightDiff = element.getBoundingClientRect().bottom - $(window).height();
  if (this._tmp_heightDiff > 0)
  {
    Gui.setElementPosition(element, element.getBoundingClientRect().left, element.getBoundingClientRect().top - this._tmp_heightDiff);
  }
};

/**
 * Hides displayed caption of the picked object/group under the mouse.
 */
Gui.prototype.hidePickableModelName = function()
{
  this.$pickableObjectUnderMouse.hide();
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// POSITIONING, VISIBILITY, SIZE FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Used after the application is loaded at the start (all models created etc.).
 */
Gui.prototype.showCanvas = function()
{
  $(canvas).show();

  AppLoading.end();
};

/**
 * Hides entire GUI - icons, minimap, etc.
 */
Gui.prototype.hide = function()
{
  this.$onscreenGui_elem.hide();
};

/**
 * Shows modal overlay behind the UI windows and freezes application (and vice versa).
 *
 * @param {boolean} show    TRUE if modal shall be shown, FALL if it shall be hidden
 */
Gui.prototype.showModalBehind = function (show)
{
  if (show)
  {
    if (this.mainMenu)
    {
      this.mainMenu.$modalOverlayBackground.show();

      appMain.freezeApplication();
    }
  }
  else
  {
    if (this.mainMenu)
    {
      this.mainMenu.$modalOverlayBackground.hide();

      appMain.unfreezeApplication();
    }
  }
};

/**
 * Shortcut for positioning element in HTML page.
 *
 * @param {HTMLElement}   element   HTML element
 * @param {number}        x         new X position
 * @param {number}        y         new Y position
 */
Gui.setElementPosition = function (element, x, y)
{
  element.style.left = x + "px";
  element.style.top  = y + "px";
};

/**
 * Shortcut for changing element dimensions.
 *
 * @param element           HTML element
 * @param {number} width    new element width
 * @param {number} height   new element height
 */
Gui.setElementSize = function (element, width, height)
{
  element.style.width  = width + "px";
  element.style.height = height + "px";
};

/**
 * Hides WebGL canvas and makes auxiliary 2D canvas visible. Used for debugging.
 */
Gui.showCanvas2d = function ()
{
  canvas.style.display    = "none";
  canvas.style.visibility = "hidden";

  // need to be both displayed and visible
  canvas2d.style.display    = "block";
  canvas2d.style.visibility = "visible";
};

/**
 * Changes size of 2D auxiliary canvas.
 *
 * @param {number} width
 * @param {number} height
 */
Gui.setCanvas2dSize = function (width, height)
{
  // in this case, change only element attributes, not style.width or style.height!
  canvas2d.width  = width;
  canvas2d.height = height;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DISPLAY WINDOW
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Retrieves container with information about the browser window.
 *
 * @returns {Win}
 */
Gui.prototype.getDisplayWindow = function()
{
  return this.windowDisplay;
};

/**
 * @returns {THREE.Vector2}    ratio between original (application) window dimension and rendering window (CANVAS element CSS size) dimension
 */
Gui.prototype.getWindowOriginalToRenderRatio = function()
{
  return this.windowOriginalToRenderRatio;
};

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

/**
 * Returns computation map object, i.e. the one used for heightmaps for coordinates conversions and such.
 * @returns {Map}
 */
Gui.prototype.getUnderlyingMap = function()
{
  return this.minimap.getUnderlyingMap();
};

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

/**
 * Makes the element not draggable.
 *
 * @param {jQuery|HTMLElement} element
 */
Gui.makeElementUndraggable = function (element)
{
  var $elem = element;
  if (!($elem instanceof jQuery))
  {
    $elem = $(element);
  }

  $elem.attr("ondragstart", "return false");
};

/**
 * Makes the element draggable (again).
 *
 * @param {jQuery|HTMLElement} element
 */
Gui.makeElementDraggable = function (element)
{
  var $elem = element;
  if (!($elem instanceof jQuery))
  {
    $elem = $(element);
  }

  $elem.removeAttr("ondragstart");
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CSS CLASS CHANGE - VIRTUAL KEYBOARD
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Makes virtual keyboard buttons to draw themselves in red.
 */
Gui.prototype.drawVirtualKeyboardAsForbidden = function()
{
  this.$virtualKeyboardUp_elem.addClass("gui-keyboard-button-rectangle-forbidden");
  this.$virtualKeyboardDown_elem.addClass("gui-keyboard-button-rectangle-forbidden");
  this.$virtualKeyboardLeft_elem.addClass("gui-keyboard-button-rectangle-forbidden");
  this.$virtualKeyboardRight_elem.addClass("gui-keyboard-button-rectangle-forbidden");
};

/**
 * Restores normal visual style of virtual keyboard.
 */
Gui.prototype.restoreVirtualKeyboardNormalCss = function()
{
  this.$virtualKeyboardUp_elem.removeClass("gui-keyboard-button-rectangle-forbidden");
  this.$virtualKeyboardDown_elem.removeClass("gui-keyboard-button-rectangle-forbidden");
  this.$virtualKeyboardLeft_elem.removeClass("gui-keyboard-button-rectangle-forbidden");
  this.$virtualKeyboardRight_elem.removeClass("gui-keyboard-button-rectangle-forbidden");
};

Gui.prototype.getMainMenu = function ()
{
	return this.mainMenu;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SVG LOADING VIA PLUGIN
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Creates a <div> wrapper for the SVG and initializes SVG jQuery plugin on it.
 *
 * @param   {string}              handlerId         which id will newly created SVG <div> wrapper obtain
 * @param   {string}              svgFilename       filename of the SVG to be loaded
 * @param   {*}                   svgLoadCallback   function which will be executed when the SVG is loaded
 * @param   {jQuery}              [parentElement]   into which element will be SVG wrapper appended
 * @param   {Asset|AssetJob}      [parentAssetJob]  a job that will wait for the SVG to load
 * @returns {*}                                     handler object which can be manipulated with SVG methods from jQuery plugin
 */
Gui.loadSvg = function (handlerId, svgFilename, svgLoadCallback, parentElement, parentAssetJob)
{
  // create <div> wrapper for svg and initialize SVG jQuery plugin on it
  var svgWrapper = $('<div id="'+handlerId+'" class="svgdiv"></div>');

  if (Util.isInitialized(parentElement))
  {
    parentElement.append (svgWrapper);
  }

  svgWrapper.svg();
  var svgHandler = svgWrapper.svg("get");

  var svgAssetJob = assetLoader.makeExternalLoadingJob ("svg " + svgFilename + " load", svgLoadCallback, {svgWrapper: svgWrapper});
  if (Util.isInitialized (parentAssetJob))
  {
    //parentAssetJob.waitFor (svgAssetJob);
    parentAssetJob.waitWithRunningForFinish(svgAssetJob);
  }

  svgHandler.load (svgFilename,
      {
        changeSize: true, // needed for correct display of the SVG
        onLoad: function (wrapperSvg, error)
        {
          Gui.makeSvgNormallyStylable (svgHandler._svg);

          // in a case of external loading job such as loading the SVG, this will effectively mark job as finished and
          // all jobs that required the svg loading job can start
          svgAssetJob.startJob();

          // (do not add anything there - there is no reason; the stuff to do after the SVG is loaded is already defined in svgLoadCallback)
        }

      });

  return svgHandler;
};

/**
 * Makes the SVG element behave normally when styling (e.g. when applied "width: 50%" on the SVG element,
 * the SVG image shall have 50% width of the container).
 *
 * @param {HTMLElement} svgDomElement
 */
Gui.makeSvgNormallyStylable = function (svgDomElement)
{
  // add viewBox attribute and copy width and height to it
  // (note: do not use jQuery for that, it lowercases the attributes, which is bad - SVG attributes are case-sensitive)
  // (note2: jQuery cannot access attributes which are not lowercase, it will return undefined)
  svgDomElement.setAttribute("viewBox", "0 0 " + svgDomElement.getAttribute("width") + " " + svgDomElement.getAttribute("height"));

  svgDomElement.setAttribute("my_original_width", svgDomElement.getAttribute("width"));
  svgDomElement.setAttribute("my_original_height", svgDomElement.getAttribute("height"));

  // remove width and height
  svgDomElement.removeAttribute("width");
  svgDomElement.removeAttribute("height");

  // voila, now we can apply style (e.g. "width: 50%") to the svgDomElement and it will resize LIKE ONE WOULD EXPECT!!!
};
