var myList = myList || { 'settings': {} };
jQuery(function($){

/**
 * Get arguments and values of specific link query string.
 *
 * @param $element
 *   DOM element that refers to link.
 * @return
 *   A property map with all arguments and values.
 */
myList.parseLinkArgs = function($element) {
  var queryString = $element.attr('href').split('?')[1];
  var args = {};
  queryString.replace(
    new RegExp('([^?=&]+)(=([^&]*))?', 'g'),
    function($0, $1, $2, $3) { args[$1] = decodeURIComponent($3); }
  );
  return args;
}

/**
 * Performs Ajax request for MyList buttons.
 *
 * @param $element
 *   Used button to trigger action.
 * @param args
 *   Map of arguments to use. It has been previous parsed from link.
 * @see myList.ajaxSuccess()
 * @see myList.widthFreeze()
 */
myList.AjaxGet = function($element, args) {
  // Freezes the aspect of the element, stores this content, replace content
  // by the ajax-loading indicator, make the ajax success processes, unfreeze
  // the aspect of the element and if the element's content has not been
  // modified by the ajax success processes, restore the content that there had
  // been before replaced by the ajax-loading indicator.
  //
  // Freezing the aspect of the element is made in order to avoid hopping of
  // rest elements on the design.
  myList.aspectFreeze($element, true);
  var isTextButton = $element.closest('.button_flexible').size() ? true : false;
  var oldContent = $element.html();
  if (isTextButton) {
    $element.html('<span class="ajax-loading">&nbsp;</span>');
  }
  $element.addClass('ajax-loading');
  $.getJSON(myList.settings.ajaxPath, args, function(json) {
    myList.ajaxSuccess($element, args, json);
    myList.aspectFreeze($element, false);
    $element.removeClass('ajax-loading');
    if ($element.find('span.ajax-loading').size() > 0) {
      $element.html(oldContent);
    }
  });
}

/**
 * Perform some actions when AJAX request is complete.
 *
 * @param $button
 *   DOM element which is the action button.
 * @param args
 *   Arguments used in AJAX request, previously parsed from link.
 * @param data
 *   Returned data from AJAX request.
 */
myList.ajaxSuccess = function($button, args, json) {
  switch (args['action']) {
    case 'add':
      myList.ajaxSuccessAdd($button, args, json);
      break;
    case 'del':
      myList.ajaxSuccessDel($button, args, json);
  }
}

/**
 * Actions for AJAX request completion at 'add' action.
 *
 * @see myList.ajaxSuccess()
 */
myList.ajaxSuccessAdd = function($button, args, status) {
  var settings = myList.settings;

  switch (status) {
    // Need new list. Prompt to user and if confirmed then reset the counter
    // present on My List menu item and make the Ajax request again attaching
    // now a flag refering to this confirmation.
    case -4:
      myList.popup($button, 'top', settings.anotherListText, {
        'yes': {
          'title': settings.yesText,
          'func': function() {
            myList.updateCounter('reset');
            myList.AjaxGet($button, $.extend(args, {'create_new': true}));
          }
        },
        'no': {
          'title': settings.noText
        }
      });
      break;

    // MyList is full. Notify to user.
    case -3:
      myList.popup($button, 'top', settings.listFullText);
      break;

    // Item already exists.
    case -1:

    // With this item list becomes full.
    case -2:

    // Added succesfully.
    case true:

      // Switch button to 'del';
      myList.toggleAddDel($button, args);

      // Disable add buttons if with this item list becomes full.
      if (status == -2) {
        myList.disableAddButtons();
      }

      // Fly apartment picture or apartment price box to My List menu item.
      var $apartment = $button.closest('.apart').find('.apartpic a img');
      if (!$apartment.size()) {
        var $apartment = $button.closest('#step3').find('.step3ins');
      }
      myList.flyToMenu($apartment, function() {
        myList.updateCounter('add');
      });

      break;

    // Error.
    default:
  }
}

/**
 * Actions for AJAX request completion at 'del' action.
 *
 * @param $button
 * @param args
 * @param data
 * @see myList.ajaxSuccess()
 */
myList.ajaxSuccessDel = function($button, args, data) {
  if (data === true) {
    // If arrival arg is present this means that we clicked
    // "delete from mylist" from converted add button. In this case
    // just make few actions and return.
    if (typeof args.arrival != 'undefined') {
      myList.toggleAddDel($button, args);

      // Fly apartment picture or apartment price box from My List menu item.
      var $apartment = $button.closest('.apart').find('.apartpic a img');
      if (!$apartment.size()) {
        var $apartment = $button.closest('#step3').find('.step3ins');
      }
      myList.flyFromMenu($apartment, function() {
        myList.updateCounter('del');
      });

      myList.enableAddButtons();
      return;
    }

    myList.updateCounter('del');

    var $wrapper = $button.parents('.wrapper');

    $mylistItem = $button.parents('.mylist-item');
    $mylistItemPrev = $mylistItem.prev('.mylist-item');

    // Append temporally classes in order to make more beautiful the borders.
    if ($mylistItemPrev.size()) {
      $mylistItemPrev.addClass('last');
      $mylistItem.addClass('deleting');
    }

    $mylistItem.fadeTo(800, 0, function() {
      if ($('.mylist-item:not(.processing)').size() == 1) {
        $wrapper.find('.mylist-mysearch').fadeOut();
        $wrapper.find('.mylist-send-n-print').fadeOut();
        $wrapper.find('.mylist-items').fadeOut();
        $wrapper.append('<div class="mylist-empty" style="display:none"><p>' + myList.settings.emptyText + '</p></div>');
        $wrapper.find('.mylist-empty').fadeIn();
        $(this).remove();
      }
      else {
        $(this).addClass('processing').animate({
          'width': '0px'
        },
        {
          duration: 1200,
          easing: 'easeOutBounce',
          complete: function() {
            $(this).remove();
            $mylistItemPrev.removeClass('last');
            $wrapper.find('.mylist-item').last().addClass('last');
          }
        })
      }
    });
  }
}

/**
 * Convert a 'add' button to 'del and vice versa.
 */
myList.toggleAddDel = function($element, args) {
  var settings = myList.settings;
  if (args.action == 'add') {
    args.action = 'del';
    $element.attr('title', settings.deleteFromTitle);
  }
  else {
    args.action = 'add';
    $element.attr('title', settings.addLabelTitle);
  }

  // Update link path, class and content.
  var isTextButton = $element.closest('.button_flexible').size() ? true : false;
  $element.attr('href', settings.path + '?' + $.param(args))
    .toggleClass('mylist-delete-from')
    .html(myList.toogleAddDelGetContent(args.action, isTextButton))

  if (!isTextButton) {
    var baseClass = 'mylist-flat-button-';
    $element
      .toggleClass(baseClass + 'add')
      .toggleClass(baseClass + 'del');
  }
}

myList.toogleAddDelGetContent = function(action, isTextButton) {
  if (isTextButton) {
    return (action == 'add') ? myList.settings.addLabel : myList.settings.deleteFromLabel;
  }
  return '';
}

/**
 * Disable 'Add' buttons.
 */
myList.disableAddButtons = function() {
  var $deleteButtons = $('a.mylist-ajaxified:not(.mylist-delete-from)');

  $deleteButtons
    .addClass('disabled')
    .attr('title', myList.settings.listFullText);

  $deleteButtons
    .parent('div.button_flexible')
    .addClass('disabled');
}

/**
 * Enable 'Add' buttons.
 */
myList.enableAddButtons = function() {
  var $deleteButtons = $('a.mylist-ajaxified:not(.mylist-delete-from)');

  $deleteButtons
    .removeClass('disabled')
    .attr('title', myList.settings.addLabelTitle);

  $deleteButtons
    .parent('div.button_flexible')
    .removeClass('disabled');
}

/**
 * Updates the counter of My List link.
 *
 * @param action
 *   May be 'add', 'del' or 'reset'.
 */
myList.updateCounter = function(action) {
  var $counter = $('#mylist_counter');

  // Reset action.
  if (action == 'reset') {
    var text = 0;
  }
  // Add and del action.
  else {
    var addition = (action == 'add') ? 1 : -1;
    var text = parseInt($counter.text()) + addition;
  }

  $counter.text(text);
}


/**
 * Freezes the aspect of an element allowing to replace its content by another
 * which have less lenght keeping the same size of the wrapper.
 *
 * @param $element
 *   DOM element to freeze.
 * @param freeze
 *   True to freeze and False to unfreeze.
 */
myList.aspectFreeze = function($element, freeze) {
  // Freeze is the same as saying set the 'width' and 'height' attribute as
  // element really has in the box model of the document.
  if (freeze) {
    //XHTML Strict:
    //var width = ($.browser.msie) ? ($element.outerWidth()) + 'px' : $element.width();
    $element.css('width', $element.width() + 'px');
    $element.css('height', $element.height() + 'px');
  }

  // Unfreeze, is the same as saying remove 'width' and 'height' attribute.
  else {
    $element.css('width', '');
    $element.css('height', '');
  }
}

/**
 * Open popup near to an element.
 *
 * @param $element
 *   Popup appears near this element.
 * @param body
 *   Popup body.
 * @param buttons
 *   Map defining buttons like this example:
 *   @code
 *     {
 *       yes: {
 *         title: 'Yes',
 *         func: function() { alert('hello') }
 *       },
 *       no {
 *         title: 'No'
 *       }
 *     }
 *   @endcode
 *   
 *   Default value if not present is an "Ok" button.
 * @param autoClose
 *   Boolean value which defines if popup will be closed after a period of
 *   time. Default value is true if buttons argument is not present.
 * @param close
 *   Boolean value which defines if popup closes after button function
 *   procedures. Default value is true.
 */
myList.popup = function($element, position, body, buttons, autoClose, close) {
  // Default autoClose is false. If not buttons are defined, default value is
  // true.
  var autoClose = autoClose || false;
  if (typeof buttons == 'undefined') {
    var autoClose = true;
  }

  // Default value of close is true.
  if (typeof close == 'undefined') {
    var close = true;
  }

  // Default buttons if not passed.
  var buttons = buttons || {
    'ok': {
      'title': myList.settings.okText
    }
  }

  // Close function.
  var popupClose = function() {
    // Clear autoclose timeouts to avoid closing others popup opened after close
    // this.
    if ((myList.popupTimerClose)) {
      clearTimeout(myList.popupTimerClose);
    }

    $('#popup').fadeOut('fast', function() {
      $(this).remove();
    });
  }

  // Create popup function.
  var popupCreate = function($element, position, body, buttons) {

    // Start creating popup HTML selector.
    var dialog = '<div id="popup" class="' + position + '" style="display: none;"><div class="wrapper"><div class="popup-body">' + body + '</div><div class="popup-buttons">';

    // Attach buttons.
    for (id in buttons) {
      dialog += '<button id="popup-button-' + id + '"';
      if ((buttons[id].disabled)) {
        dialog += ' disabled="disabled"';
      }
      dialog += '>' + buttons[id].title + '</button>';
    }

    dialog += '</div></div><div class="footer"></div></div>';

    $('body').append(dialog);

    // Attach button functions.
    for (id in buttons) {
      $('#popup-button-' + id).click(id, function(e) {
        var func = buttons[e.data].func;
        if (typeof func == 'function') {
          func();
          if (close) {
            popupClose();
          }
        }
        else {
          popupClose();
        }
      });
    }

    $('#popup').fadeIn('fast'); 
  }

  // If previous popup is open, close it.
  if ($('#popup').size() > 0) {
    popupClose();
  }

  // Create popup with passed options.
  popupCreate($element, position, body, buttons);

  // Use jQury UI position to set position.
  var positionArgs = {
    of: $element
  }
  if (position == 'top') {
    positionArgs.my = 'center bottom';
    positionArgs.at = 'center top';
  }
  else if (position == 'left') {
    positionArgs.my = 'right middle';
    positionArgs.at = 'center left';
    positionArgs.offset = '-13 -10';
  }
  $('#popup').position(positionArgs);

  if (autoClose) {
    // Reset autoclose timer to avoid duplicate behaviors width fast-clicking
    // users.
    if ((myList.popupTimerClose)) {
      clearTimeout(myList.popupTimerClose);
    }

    myList.popupTimerClose = setTimeout(popupClose, 8000);
  }
}

/**
 * Send by Email callback function for button action.
 *
 * Open a popup which prompt e-mail to user.
 *
 * @param $element
 *   DOM element which is the trigger button.
 * @param
 */
myList.sendByEmail = function($element, args) {
  var settings = myList.settings;
  var body = '<div class="mylist-sendfriend-mail-wrapper">' + settings.sendbyemailBody + '<form action=""><input id="mylist-sendtofriend-mail" type="text" value="' + settings.sendbyemailInput + '" /></form></div>';
  myList.popup($element, 'left', body, {
    'submit': {
      'title': 'Submit',
      'func': function() {
        var mail = $('#mylist-sendtofriend-mail').val();
        if (!myList.validateMail(mail)) {
          alert('Please enter a valid e-mail.');
          $('#mylist-sendtofriend-mail').focus();
          return;
        }
        var $mailWrapper = $('#popup .mylist-sendfriend-mail-wrapper');
        $mailWrapper.html('<span class="ajax-loading">&nbsp;</span>');
        $.getJSON(settings.ajaxPath, $.extend(args, {'mail': mail}), function(json) {
          $('#popup-button-submit').fadeOut('fast');
          $('#popup .wrapper .popup-body').text(json);
        });
      }
    },
    'close': {
      'title': 'Close'
    }
  }, false, false);

  // Define e-mail input event handler.
  var sendtofriendMailEvent = function(event) {
  var $element = $(this);
  switch (event.type) {

    // Remove default value on **focus**.
    case 'focus':
      if ($element.val() == settings.sendbyemailInput) {
        $element.val('');
      }
      break;

    // Set default value on **blur** if not value.
    case 'blur':
      if ($.trim($element.val()) == '') {
        $element.val(settings.sendbyemailInput);
      }
      break;

    // Validate e-mail and handle enter on **keyup**.
    case 'keydown':
      var $submit = $('#popup-button-submit');
      // Handle 'enter' as click on the submit button.
      if (event.keyCode == 13) {
        event.preventDefault();
        $submit.click();
      }
    }
  }

  // Attach event handler.
  $('#mylist-sendtofriend-mail').bind('blur focus keydown', sendtofriendMailEvent);
}

/**
 * Validates a e-mail.
 * [RFC 2822] Internet Message Format.
 */
myList.validateMail = function(value) {
  return /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
}

/**
 * Updates Add to My List button. This is called when calculate new price from
 * apartment sheet.
 *
 * @param $button
 *   DOM element which is the 'Add to My List' button.
 * @param json
 *   JSON data returned from AJAX request which updates prices.
 * @see calculate()
 * @see montarPrecios()
 */
myList.updateAddButton = function($button, json) {
  var settings = myList.settings;

  if (!settings.hasOwnProperty('zoneID')) {
    return;
  }

  var args = {
    action: 'add',
    id: json.id_apartamento,
    arrival: json.entrada,
    departure: json.salida,
    adults: json.adultos,
    destination: settings.zoneID
  }

  var $link = $button.find('a');
  var label = settings.addLabel;
  switch (json.mylist) {
    case -2:
    case -3:
      $button.addClass('disabled');
      break;

    // Item previously added. Switch to 'del' action.
    case -1:
      args.action = 'del';    
      var label = settings.deleteFromLabel;
      $link
        .addClass('mylist-delete-from')
        .attr('title', settings.deleteFromTitle);

    // 'Needly of new list' or 'free to add' status.
    case -4:
    case true:
    default:   
      $button.removeClass('disabled');
      $link.addClass('mylist-ajaxified')
        .attr('href', settings.path + '?' + $.param(args))
        .text(label);
  }
}

myList.flyToMenu = function ($apartment, complete) {
  var $apartmentFly = $apartment.clone();
  var apartmentOffset = $apartment.offset();

  var $menuItem = $('#mylist_counter').closest('a');
  var menuItemOffset = $menuItem.offset();

  $apartmentFly
    .css({
      'position': 'absolute',
      'top': apartmentOffset.top,
      'left': apartmentOffset.left,
      'z-index': 9999
    })
    .appendTo('body')
    .animate({
      'height': $menuItem.height(),
      'left': menuItemOffset.left,
      'opacity': 0.25,
      'top': menuItemOffset.top,
      'width': $menuItem.width()
    }, 2800, function() {
      $apartmentFly.remove();
      if (typeof complete == 'function') {
        complete();
      }
    });
}

myList.flyFromMenu = function ($apartment, complete) {
  var $apartmentFly = $apartment.clone();
  var apartmentOffset = $apartment.offset();

  var $menuItem = $('#mylist_counter').closest('a');
  var menuItemOffset = $menuItem.offset();

  $apartmentFly
    .css({
      'height': $menuItem.height(),
      'left': menuItemOffset.left,
      'position': 'absolute',
      'top': menuItemOffset.top,
      'width': $menuItem.width(),
      'z-index': 9999
    })
    .appendTo('body')
    .animate({
      'height': $apartment.height(),
      'left': apartmentOffset.left,
      'opacity': 0.25,
      'top': apartmentOffset.top,
      'width': $apartment.width()
    }, 2800, function() {
      $apartmentFly.remove();
      if (typeof complete == 'function') {
        complete();
      }
    });
}

/**
 * Trigger for ajaxified link.
 */
$('a.mylist-ajaxified').click(function(e) {
  var $element = $(this);

  // By now only 'Add to MyList' buttons can be disabled. So we use this
  // condition to alert that 'My List is full'.
  if ($element.hasClass('disabled')) {
    myList.popup($element, 'top', myList.settings.listFullText);
  }

  else if (!$element.hasClass('ajax-loading')) {

    // Extract args from link URL and make AJAX request with them.
    var args = myList.parseLinkArgs($element);

    switch(args.action) {

      // Print document.
      case 'print':
        window.print();
        break;

      // Send by E-mail.
      case 'sendbyemail':
        myList.sendByEmail($element, args);
        break;

      // Another button that directly makes an Ajax request.
      default:
        myList.AjaxGet($element, args);

    }
  }

  e.preventDefault();
});

});

