/**
 * Definition of constants
 */
const EWAJAX_ANCHOR_ELEMENT_STRING = "HTMLAnchorElement";
const EWAJAX_INPUT_ELEMENT_STRING = "HTMLInputElement";
const EWAJAX_FORM_ELEMENT_STRING = "HTMLFormElement";

/*
 * Reload mapping. If the triggered element has a 
 * "data-ajaxreloadmapping" attribute which is specified 
 * as an property in this object, the loading animation 
 * and the reload of the content will only be performed
 * for the elements which are defined in the corresponding
 * property array.
 */
const EWAJAX_RELOAD_ELEMENT_MAPPING = {
    "subcategory": [
        "#ewpagepath",
        "#ewlist",
        "#ewleftmenu-catdesc",
        "#ewleftmenu-catbrands"
    ],
    "subcategoryaction": [
        "#ewpagepath",
        "#ewlist"
    ]
};

/*
 * Callback mapping. If the triggered element has a 
 * "data-ajaxcallbackmapping" attribute which is specified 
 * as an property in this object, the callback functions 
 * which are defined in the corresponding property array 
 * will be executed after the response is received.
 */
const EWAJAX_CALLBACK_MAPPING = {
    "topnavigationcategory": [
        "selectTopCategory"
    ]
};

/**
 * Global variables
 */
var ajaxificationRequestId = 0;
var ajaxificationTriggerElement = null;

/**
 * Bind ajaxification to the elements on document ready.
 */
$(document).ready(ewbindajaxification);

/**
 * Bind the events.
 */
function ewbindajaxification() {
  $(".ewajaxified").each(function() {
    // check if the event for this element was already bind
    if ($(this).data("ajaxified")) {
        // continue
        return true;
    } else {
        $(this).data("ajaxified", true);
    }
    
    var oElement = $(this).get(0);
    var sTypeOfElement = oElement.constructor.toString();
    if (sTypeOfElement.indexOf(EWAJAX_ANCHOR_ELEMENT_STRING) != -1) {
      $(this).click(function(event) { event.preventDefault(); ewrunajaxification(oElement); });
    }
    else if (sTypeOfElement.indexOf(EWAJAX_INPUT_ELEMENT_STRING) != -1) {
      var result = $(this).attr("onclick").toString().match(/location = (\'.*\')/);
      if (!result) {
        result = $(this).attr("onclick").toString().match(/location = (\".*\")/);
      }
      if (result) {
        var sOriginalLocation = result[1];
        sOriginalLocation = sOriginalLocation.replace("this", "!oElement");
        sOriginalLocation = eval(sOriginalLocation);
        $(this).attr("onclick", "");
        $(this).click(function(event) { event.preventDefault(); ewrunajaxification(oElement, sOriginalLocation); });
      }
    }
    else if (sTypeOfElement.indexOf(EWAJAX_FORM_ELEMENT_STRING) != -1) {
      $(this).submit(function(event) { event.preventDefault(); ewrunajaxification(oElement); });
    }
    else {
      //alert("unknown: " + sTypeOfElement);
      //return true;
    }
  });
  
  // Check if ajaxified elements are defined
  //if ($('.ewajaxified').length > 0) {
    // Bind onscroll event to center loading gif
    $(window).scroll(function () {
      if ($(".ewajaxificationwait .ewloadimage").length > 0) {
        // calculate new pos only every 10ms
        if (typeof ewajaxificationloadingimgposcalc == "undefined" || ewajaxificationloadingimgposcalc == null) {
          ewajaxificationloadingimgposcalc = new Date().getTime();
        } else if (new Date().getTime() > ewajaxificationloadingimgposcalc + 10) {
          ewajaxificationloadingimgposcalc = new Date().getTime();
        } else {
          return;
        }
        
        // scroll top pos
        var viewpointTopPos = $("html").scrollTop();
        
        // workaround for webkit
        if ($("body").scrollTop() > viewpointTopPos) {
          viewpointTopPos = $("body").scrollTop();
        }
        
        // browser window height
        var viewportHeight = $(window).height();
        
        $(".ewajaxificationwait").each(function() {
          // determine the top pos of the wait container
          var waitContainerTopOffset = $(this).offset().top;
          var waitContainerHeight = $(this).innerHeight();
          
          var calculatedPos = viewportHeight/2 + viewpointTopPos - waitContainerTopOffset;
          
          if (calculatedPos < 0) {
            calculatedPos = 0;
          } else if (calculatedPos > waitContainerHeight) {
            calculatedPos = waitContainerHeight;
          }
          
          $(this).find(".ewloadimage:first").css("top", calculatedPos + "px");
        });
      }
    });
  //}
}

/**
 * Run the ajaxification for the given element.
 */
function ewrunajaxification(oElement, sOriginalLocation) {
  
  // create an ajaxification object an start running
  var oAjaxification = new ewAjaxification();
  if (typeof(sOriginalLocation) != "undefined") oAjaxification.sLocation = sOriginalLocation;
  return oAjaxification.run(oElement);
}

/**
 * Array with event handlers.
 */
var aAJAXReadyFunctions = new Array();

/**
 * The class ewAjaxification.
 */
function ewAjaxification() {
  
  // the element which triggered the ajax call
  this.oTriggeredElement = null;
  
  // the original location to go to
  this.sLocation = null;
  
  // reload mapping identifier
  this.sReloadMappingIdentifier = null;
  
  // callback mapping identifier
  this.sCallbackMappingIdentifier = null;
    
  // main run function
  this.run = function(oElement) {
    var oThisObject = this;
    var oThisElement = oElement;
    
    this.oTriggeredElement = oThisElement;
    
    if (typeof $(oElement).data("ajaxreloadmapping") != "undefined") {
        this.sReloadMappingIdentifier = $(oElement).data("ajaxreloadmapping");
    }
    
    if (typeof $(oElement).data("ajaxcallbackmapping") != "undefined") {
        this.sCallbackMappingIdentifier = $(oElement).data("ajaxcallbackmapping");
    }
    
    this.showWaitEffect(function() { oThisObject.start(oThisElement); });
  }
    
  // start ajaxification, switch type of element
  this.start = function(oElement) {
    var sTypeOfElement = oElement.constructor.toString();
    if (sTypeOfElement.indexOf(EWAJAX_ANCHOR_ELEMENT_STRING) != -1) {
      ajaxificationRequestId++;
      this.runOnAnchor(oElement);
    }
    else if (sTypeOfElement.indexOf(EWAJAX_INPUT_ELEMENT_STRING) != -1) {
      ajaxificationRequestId++;
      this.runOnInputElement();
    }
    else if (sTypeOfElement.indexOf(EWAJAX_FORM_ELEMENT_STRING) != -1) {
      ajaxificationRequestId++;
      this.runOnForm(oElement);
    }
    else {
      //alert("unknown: " + sTypeOfElement);
      return true;
    }
    
    return false;
  }
  
  // run ajaxification on anchor
  this.runOnAnchor = function(oAnchor) {
    var oAjaxification = this;
    $.ajax({
      type:     "GET",
      url:      oAnchor.href + (oAnchor.href.indexOf("?") !== -1 ? "&" : "?") + "ajaxified=1&requestid=" + ajaxificationRequestId,
      dataType: "json",
      success:  function(data) { oAjaxification.handleSuccessfulResult(data); },
      error: function(response) { /*console.log(response);*/ }
    });
  }
  
  // run on input element, we already have the location string
  this.runOnInputElement = function() {
    var oAjaxification = this;
    $.ajax({
      type:     "GET",
      url:      oAjaxification.sLocation + (oAjaxification.sLocation.indexOf("?") !== -1 ? "&" : "?") + "ajaxified=1&requestid=" + ajaxificationRequestId,
      dataType: "json",
      success:  function(data) { oAjaxification.handleSuccessfulResult(data); },
      error: function(response) { /*console.log(response);*/ }
    });
  }
  
  // run ajaxification on from
  this.runOnForm = function(oForm) {
    var oAjaxification = this;
    
    // build temp array
    var aPostData = new Object();
    $.each(oForm.elements, function(key, value) {
      // @TODO: wir müssen hier irgendwie sicherstellen, dass doppelte werte (i.e. checkbox + hidden field) nicht doppelt in den query string kommen
      if (aPostData[value.name] == undefined) {
        if (value.name.length > 0) {
          aPostData[value.name] = value.value;
        }
      }
      else {
        if (value.checked) {
          aPostData[value.name] = value.value;
        }
      }
    });
    
    // create the final request string now
    var sPostData = "";
    $.each(aPostData, function(key, value) {
      if (sPostData.length > 0) sPostData += "&";
      sPostData += key + "=" + value;
    });
    sPostData += "&ajaxified=1&requestid=" + ajaxificationRequestId;
    
    // send request
    $.ajax({
      type:     "POST",
      url:      sShopBaseUrl + "index.php",
      data:     sPostData,
      dataType: "json",
      success:  function(data) { oAjaxification.handleSuccessfulResult(data); },
      error: function(response) { /*console.log(response);*/ }
    });
  }
  
  // shows the wait effect 
  this.showWaitEffect = function(fCallback) {
    var sLoadOverlay = '<div class="ewloadoverlay"></div><img class="ewloadimage" src="' + sImageDir + 'ajaxloader-1_d3031c.gif">';
    
    if (this.sReloadMappingIdentifier != null && $.isArray(EWAJAX_RELOAD_ELEMENT_MAPPING[this.sReloadMappingIdentifier])) {
        for (var i in EWAJAX_RELOAD_ELEMENT_MAPPING[this.sReloadMappingIdentifier]) {
            $(".ewajaxificationwait#" + EWAJAX_RELOAD_ELEMENT_MAPPING[this.sReloadMappingIdentifier][i])
                .children(".ewloadoverlay, .ewloadimage")
                .remove();
            
            $(".ewajaxificationwait#" + EWAJAX_RELOAD_ELEMENT_MAPPING[this.sReloadMappingIdentifier][i])
                .append(sLoadOverlay);
        }
    } else {
        $(".ewajaxificationwait")
            .children(".ewloadoverlay, .ewloadimage")
            .remove();
        
        $(".ewajaxificationwait")
            .append(sLoadOverlay);
    }
    
    // trigger window scroll event to calculate the loading gif position
    $(window).trigger("scroll");
    
    fCallback();
  }
  
  // successful result handler
  this.handleSuccessfulResult = function(oResultData) {
        var oTriggeredElement = this.oTriggeredElement;
        var sReloadMappingIdentifier = this.sReloadMappingIdentifier;
        var sCallbackMappingIdentifier = this.sCallbackMappingIdentifier;
        
        // check if no newer request was submitted
        if (oResultData["requestid"] < ajaxificationRequestId) {
            return;
        }
        
        $.each(oResultData, function(sKey, sValue) {
            if (
                sKey != "requestid" &&
                (sReloadMappingIdentifier === null || 
                !$.isArray(EWAJAX_RELOAD_ELEMENT_MAPPING[sReloadMappingIdentifier]) || 
                $.inArray(sKey, EWAJAX_RELOAD_ELEMENT_MAPPING[sReloadMappingIdentifier]) !== -1)
            ) {
                $(sKey).empty().append(sValue);
            }
        });
        
        // check if callback functions are defined
        if (
            sCallbackMappingIdentifier !== null &&
            $.isArray(EWAJAX_CALLBACK_MAPPING[sCallbackMappingIdentifier])
        ) {
            // run through all callback functions
            for (i in EWAJAX_CALLBACK_MAPPING[sCallbackMappingIdentifier]) {
                // execute callback function
                this[EWAJAX_CALLBACK_MAPPING[sCallbackMappingIdentifier][i]](oTriggeredElement);
            }
        }
        
        ewbindajaxification();
        
        // bind other event handlers
        for (var i = 0; i < aAJAXReadyFunctions.length; i++) {
            var currentfunction = aAJAXReadyFunctions[i];
            currentfunction();
        }
  }
  
  /***********************************
  *
  * Callback functions
  * 
  * see EWAJAX_CALLBACK_MAPPING
  *
  **********************************/
  
  // select (mark as selected) the current top category 
  this.selectTopCategory = function(oTriggeredElement) {
        // determine the navigation item which should be selected
        var oNavElementToSelect = $(oTriggeredElement).parents("li.menuitem:first");
        
        // get the navigation container
        var oNavContainer = $(oTriggeredElement).parents("ul.menu:first");
        
        // remove selection of all navigation elements except of the current one
        oNavContainer
            .find("li.menuitem")
            .not(oNavElementToSelect)
            .children("a")
            .removeClass("exp");
        
        // select the current navigation element
        oNavElementToSelect
            .children("a")
            .addClass("exp");
  }
}

/**
 * Bind ajaxification post loading to the elements on document ready.
 */
$(document).ready(ewajaxification_postloading);

/**
 * Function to process post loading.
 */
function ewajaxification_postloading() {
    
    // display dummy content
    $(".ajaxpostloadingdummycontent").css("display", "block");
    
    // check if post loading has to be processed
    var bDoPostLoading = false;
    $(".ajaxification_postload").each(function() {
        bDoPostLoading = true;
    });
    
    // do the post loading now
    if (bDoPostLoading) {
        
        // construct url
        var sUrl = document.URL;
        if (sUrl.indexOf("?") !== -1) {
            sUrl += "&";
        }
        else {
            sUrl += "?";
        }
        sUrl += "ajaxified=1&postloaded=1";
        
        // run ajaxification of post loaded element
        var oAjaxification = new ewAjaxification();
        $.ajax({
            type:     "GET",
            url:      sUrl,
            dataType: "json",
            success:  function(data) { oAjaxification.handleSuccessfulResult(data);},
            error: function(response) { /*console.log(response);*/ }
        });
    }
}
