/* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. */
/* ------------------------------------------------------ */
/* ------------------- amx-listView.js ------------------ */
/* ------------------------------------------------------ */

(function() {
// TODO finish the migration from "amx.*" to "adf.mf.api.amx.*" and "adf.mf.internal.amx.*"

  var amxRenderers = {

    listView:{
      create: function(amxNode) {
        var $listView = $('<div class="amx-scrollable"></div>');
        var listViewAmXNode = amxNode;
        var headerDfd, facetHeaderDfd;
        var oldValue;

        var lastDivider, dividerAttrEl, $dividerItems;
        var subCollectionStart = -1, totalItems;
        if (amxNode.dividerAttribute){
          dividerAttrEl = "#{" + amxNode.variable + "." + amxNode.dividerAttribute + "}";
        }
        if (amxNode.facets.header) {
          var $header = $("<div class='amx-listView-header'></div>");

          if (amxNode.facets.header){
            var $facetHeader = $("<div class='amx-listView-facet-header'></div>").appendTo($header);
            facetPrimaryDfd = amx.renderSubNodes(amxNode.facets.header,$facetHeader);
          }

          headerDfd = $.when(facetHeaderDfd).done(function(){
            $listView.append($header);
          });
        }
        
        var tempFetchSize = Infinity;
        if (amxNode.fetchSize && adf.mf.internal.amx.isFiniteNumber(amxNode.fetchSize))
        {
          tempFetchSize = amxNode.fetchSize;
          if (tempFetchSize < 0)
          {
            tempFetchSize = Infinity;
          }
          else if (tempFetchSize == 0)
          {
            tempFetchSize = 25;
          }
        }
        amxNode.fetchSize = tempFetchSize;
        if (amxNode._xmlNode.hasAttribute("maxRows") == false)
        {
          amxNode._xmlNode.setAttribute("maxRows", tempFetchSize);
        }
        var maxRows = amxNode._xmlNode.getAttribute("maxRows");
        
        handleTap($listView);

        amx.enableSwipe($listView,".amx-listItem");
        amx.enableTapHold($listView,".amx-listItem");

        // NOTE 1) If the headerDfd is undefined (not facets), the $.when will be immediately resolved (so it works both case)
        // NOTE 2) This return a differed and not the $node. We are using the pipe() method to filter the resolution of the return resolved.
        return $.when(headerDfd).pipe( function() {
          var listItemXmlNode = $(amxNode._xmlNode).children("amx\\:listItem")[0];
          var dataItems = amxNode.value;

          // right now, we assume this is synchronous
          //adf.mf.el.addVariable(amxNode.variable, {});

          var dataItem,row,$listItem;

          $listView.tap(".amx-listItem",function(){
            if (amx.acceptEvent()){
              var $listItem = $(this);
              var newValue = undefined;
              if ($listItem.data().amxVar !== undefined) {
                newValue = $listItem.data().amxVar.value.key;
              }
              var se = new amx.SelectionEvent(oldValue);
              amx.processAmxEvent($listView,"selection", undefined, undefined, se);
              oldValue = newValue;
            }
          });

          var appendFooter = function(){
            var $footer = $("<div class='amx-listView-footer'></div>");
            var $facetFooter = $("<div class='amx-listView-facet-footer'></div>").appendTo($footer);
            return amx.renderSubNodes(amxNode.facets.footer,$facetFooter).pipe( function() {
              $listView.append($footer);
              return $listView;
            });
          };

          var getCurrentDivider = function(i){
            var dfd = $.Deferred();
            amx.getElValue(dividerAttrEl).done(function(request, response){
              if (amxNode.dividerMode === "firstLetter"){
                dfd.resolve(response[0].value.charAt(0), i);
              } else {
                dfd.resolve(response[0].value, i);
              }
            });
            return dfd.promise();
          };

          var insertDivider = function (divider){
            var $dividerComponent = $("<div class='amx-listView-divider'></div>");
            var $dividerActual = $("<div class='amx-listView-divider-actual'></div>");
            $dividerActual.append("<div class='amx-node amx-outputText'>" + divider + "</div>");
            $dividerActual.append("<div class='amx-node amx-outputText amx-listView-divider-actual-counter'></div>");
            $dividerActual.append("<div class='amx-node amx-listView-divider-actual-indicator-expanded'></div>");
            $dividerComponent.append($dividerActual);
            $dividerItems = $("<div class='amx-listView-divider-items'></div>");
            $dividerComponent.append($dividerItems);
            $listView.append($dividerComponent);
            if (amxNode.collapsibleDividers === "true"){
              collapseDividerIfNecessary($dividerItems, divider);
              $dividerActual.tap( function() {
                if (amx.acceptEvent()){
                  var $divActual = $(this);
                  $divActual.parent().children(".amx-listView-divider-items").toggle();
                  $indicator = $divActual.children(".amx-listView-divider-actual-indicator-expanded");
                  if ($indicator.length > 0){
                    $indicator.replaceWith("<div class='amx-node amx-listView-divider-actual-indicator-collapsed'></div>");
                  } else {
                    $indicator = $divActual.children(".amx-listView-divider-actual-indicator-collapsed");
                    $indicator.replaceWith("<div class='amx-node amx-listView-divider-actual-indicator-expanded'></div>");
                  }
                }
              });
            }
            return $dividerItems;
          };

          var displayDividerCount = function (count){
            var $divCounter = $dividerItems.parent().children(".amx-listView-divider-actual").children(".amx-listView-divider-actual-counter");
            if ($divCounter){
              $divCounter.html(count);
            }
          };

          var collapseDividerIfNecessary = function($divider, dividerTitle){
            if (amxNode.collapsedDividers){
              if ($.inArray(dividerTitle, amxNode.collapsedDividers) > -1){
                $divider.hide();
                $divider.parent().children(".amx-listView-divider-actual").children(".amx-listView-divider-actual-indicator-expanded").replaceWith("<div class='amx-node amx-listView-divider-actual-indicator-collapsed'></div>");
              }
            }
          }

          // if we have a amxNode value, then we iterate
          if (adf.mf.log.Performance.isLoggable(adf.mf.log.level.FINEST))
          {
        	  adf.mf.internal.perf.trace("amxRenderers.listView", ("amxNode.value= " + amxNode.value));
          }
          if (amxNode.value){
            if (!amx.dtmode){
            	if (adf.mf.log.Performance.isLoggable(adf.mf.log.level.FINEST))
            	{
            		adf.mf.internal.perf.trace("amxRenderers.listView", 
                			  ("calling serialResolver for with " + 
                			   ((dataItems === undefined)? "*** NO dataItems defined ***" : adf.mf.util.stringify(dataItems))));
            	}
              return amx.iteratorResolve(dataItems, function(item,i)
              {
                // we set the variable
                adf.mf.el.addVariable(amxNode.variable, item);
                adf.mf.internal.perf.start(("stamping out the " + amxNode.tagName + "[" + item.index + "]"), amxNode.id);
                amx.beginIterationContainer(amxNode.id, item.rowKey());
                totalItems = i;
                if (amxNode.dividerAttribute){
                  return getCurrentDivider(i).done( function (currentDivider, currentPosition){
                    if (currentDivider !== lastDivider){
                      if ($dividerItems && amxNode.showDividerCount){
                        displayDividerCount(currentPosition - subCollectionStart);
                      }
                      subCollectionStart = i;
                      $dividerItems = insertDivider(currentDivider);
                      lastDivider = currentDivider;
                    }
                    return amx.renderNode(listItemXmlNode).done( function($listItem) {
                      // we need to store the variable as part of the $listItem for eventual later user (e.g., setPropertyListener)
                      if ($listItem != null)
                      {
                        amx.storeVariable($listItem,amxNode.variable,item);
                        if (typeof oldValue === "undefined" && $listItem.data("amxVar") != null)
                        {
                          oldValue = $listItem.data().amxVar.value.key;
                        }
                        $dividerItems.append($listItem);
                        adf.mf.el.removeVariable(amxNode.variable);
                      }
                      adf.mf.internal.perf.stop(("stamping out the " + amxNode.tagName + "[" + item.index + "]"), amxNode.id);
                      amx.endIterationContainer();
                    });
                  });
                } else {
                  return amx.renderNode(listItemXmlNode).done( function($listItem) {
                    // we need to store the variable as part of the $listItem for eventual later user (e.g., setPropertyListener)
                    if ($listItem != null)
                    {
                      amx.storeVariable($listItem,amxNode.variable,item);
                      if (typeof oldValue === "undefined" && $listItem.data("amxVar") != null)
                      {
                        oldValue = $listItem.data().amxVar.value.key;
                      }
                      $listView.append($listItem);
                      adf.mf.el.removeVariable(amxNode.variable);
                    }
                    adf.mf.internal.perf.stop(("stamping out the " + amxNode.tagName + "[" + item.index + "]"), amxNode.id);
                    amx.endIterationContainer();
                  });
                }
              }, maxRows).pipe( function()
              {
                if ($dividerItems && amxNode.showDividerCount){
                  displayDividerCount(totalItems - subCollectionStart + 1);
                }
                if (amxNode.value.hasNext())
                {
                  $moreItem = $('<div></div>');
                  $moreItem.addClass("amx-listItem");
                  $moreItem.addClass("amx-listItem-moreRows");
                  var loadMoreRowsString = adf.mf.api.amx.getInfoString("amx_listView_MSG_LOAD_MORE_ROWS");
                  $moreItemText = $('<span>' + loadMoreRowsString + '</span>');
                  $moreItemText.addClass("amx-outputText");
                  $moreItem.append($moreItemText);
                  $listView.append($moreItem);
                  $moreItem.tap( function()
                  {
                    if (amxNode.fetchSize == Infinity || maxRows == Infinity)
                    {
                      maxRows = Infinity;
                    }
                    else
                    {
                      maxRows = parseInt(maxRows) + parseInt(amxNode.fetchSize);
                    }
                    amxNode._xmlNode.setAttribute("maxRows", maxRows);
                    var dataChangeHandler = adf.mf.internal.amx.dataChangeHandlerByNode("", true);
                    dataChangeHandler($listView, 0);
                  });
                }
                // MDO - append footer
                if (amxNode.facets.footer) {
                  return appendFooter();
                }
                processEditMode($listView,listViewAmXNode);
                return $listView;
              });
            }else{
              // if we are in dtmode, just render the children as if was a static one
              return amx.renderSubNodes(amxNode,$listView).pipe(function(){
                // MDO - append footer
                if (amxNode.facets.footer) {
                  return appendFooter();
                }

                processEditMode($listView,listViewAmXNode);

                return $listView;
              });
            }
          }
          // otherwise, we render the subNodes
          else{
            return amx.renderSubNodes(amxNode,$listView).pipe(function(){
              // MDO - append footer
              if (amxNode.facets.footer) {
                return appendFooter();
              }

              processEditMode($listView,listViewAmXNode);

              return $listView;
            });
          }
        });
      },
      postDisplay: function($node,amxNode)
      {
        // Restore the old scroll position in case this view instance already had one:
        var storedData = adf.mf.api.amx.getClientState(amxNode.id);
        if (storedData != null)
        {
          var scrollLeft = storedData.scrollLeft;
          if (scrollLeft != null)
            $node.scrollLeft(scrollLeft);
          var scrollTop = storedData.scrollTop;
          if (scrollTop != null)
            $node.scrollTop(scrollTop);
        }
      },
      refresh: function($node,newAmxNode,propertiesChanged){
        // if only the .editMode property changed, then, we handle it.
        // Note 1: propertesChanged.editMode will be true if the editMode property has changed
        // Note 2: to get the value, make sure to get it from newAmxNode
        if(propertiesChanged.editMode && Object.keys(propertiesChanged).length === 1) {
          // do the code to change this $node to one mode or the other.

          var newEditMode = newAmxNode.editMode;
          if (amx.isValueTrue(newAmxNode.editMode)){
          	  adf.mf.internal.perf.start("switching to edit mode", amxNode.id);
            switchToEditMode($node);
        	  adf.mf.internal.perf.stop("switching to edit mode", amxNode.id);
          }else{
          	  adf.mf.internal.perf.start("switching to normal mode", amxNode.id);
            switchToNormalMode($node);
        	  adf.mf.internal.perf.stop("switching to normal mode", amxNode.id);
          }
          // return nothing.

        } else {
          // Save the scroll positions since this we are not using optimized refreshing:
          this._storeScrollPositions($node, newAmxNode.id);

          // so, something else changed, so, we tell the amx-core to recreate the node.
          return false;
        }
      },

      preDestroy: function($node,amxNode)
      {
        // Store off the current scroll position in case this view instance is ever revisited:
        this._storeScrollPositions($node, amxNode.id);
      },

      _storeScrollPositions: function($node, amxNodeId)
      {
        // Store off the current scroll position in case this view instance is ever revisited:
        var scrollLeft = $node.scrollLeft();
        var scrollTop = $node.scrollTop();
        if (scrollLeft != null || scrollTop != null)
        {
          var storedData =
          {
            scrollLeft: scrollLeft,
            scrollTop: scrollTop
          };
          adf.mf.api.amx.setClientState(amxNodeId, storedData);
        }
      }
    },

    listItem: function(amxNode) {
      var $node = $("<div></div>");

      var $listItemContentWrapper = $node;

      if (!amx.isValueFalse(amxNode.showLinkIcon)){
        $node.append("<div class='amx-listItem-caret'></div>");
      }

      return amx.renderSubNodes(amxNode,$listItemContentWrapper).pipe(function($node){
        return $node;
      });
    },

    iterator: function(amxNode) {
      var $iterator = $("<div></div>");

      var dataItems = amxNode.value;

      return amx.serialResolve(dataItems, function(item,i) {

      	  adf.mf.internal.perf.start(("stamping out " + amxNode.tagName + " iterator"), amxNode.id);
        adf.mf.el.addVariable(amxNode.variable, item);
        amx.beginIterationContainer(amxNode.id, item.rowKey());

        return amx.renderSubNodes(amxNode,function($subNode){
          amx.storeVariable($subNode,amxNode.variable,item);
          $iterator.append($subNode);
        }).done( function($item) {
          // we need to store the variable as part of the $listItem for eventual later user (e.g., setPropertyListener)
          adf.mf.el.removeVariable(amxNode.variable);
      	  adf.mf.internal.perf.stop(("stamping out " + amxNode.tagName + " iterator"), amxNode.id);

          amx.endIterationContainer();
        });

      }).pipe( function() {
        return $iterator;
      });

    }

  }; // /var amxRenderers

  function handleTap($listView)
  {
    $listView.tap(".amx-listItem",function()
    {
      var $node = $(this);
      if (!$listView.hasClass("amx-listView-editMode") && !$node.hasClass("amx-listItem-moreRows"))
      {
      
        // Removed the old selected state (max 1 item should be selected at a time).
        // In the future we could consider an option to allow multiple selection.
        var $oldSelection = $listView.children(".amx-listItem-selected");
        $oldSelection.removeClass("amx-listItem-selected");

        // Added a new style for the listItem that is tapped
        $node.addClass('amx-listItem-selected');
        var amxNode = $node.data("amxNode");
        // perform the tap only if the editMode is undefined or false
        var listViewAmXNode = $node.closest(".amx-listView").data("amxNode");
        if (!amx.isValueTrue(listViewAmXNode.editMode))
        {
          amx.validate($node).done(function()
          {
            if(amx.acceptEvent())
            {
              var event = new amx.ActionEvent();
              amx.processAmxEvent($node, "action", undefined, undefined, event).always(function()
              {
                if(amxNode.action)
                {
                  adf.mf.api.amx.doNavigation(amxNode.action);
                }
              });
            }
          });
        }
      }
    });
  }

  function processEditMode($listView,listViewAmXNode){
      // switch to editMode
      if (amx.isValueTrue(listViewAmXNode.editMode)){
         switchToEditMode($listView)
      }
  }

  function handleMove($node){
    var moveEvent = amx.hasTouch() ? "touchmove" : "mousemove";
    var $dropSpace = null;
    $node.find(".amx-listItem-handle").drag({
      start:function(event,dragExtra){
        $node.addClass("move");
        $dropSpace = $("<div class='amx-listItem amx-listItem-drop-spacer'></div>");
        $dropSpace.insertAfter($node);
      },
      drag:function(event,dragExtra){
        event.preventDefault();
        event.stopPropagation();
        var top = $node.offset().top;
        top = top + dragExtra.deltaPageY;
        var $parent = $node.parent();
        var parentOffset = $parent.offset();
        if(top < parentOffset.top){
          top = parentOffset.top;
        }

        //scroll view
        if(top <= parentOffset.top + 5){
          $parent.data("stop",false);
          scrollView($parent,-1);
        }else if(top + $node.height() >= parentOffset.top + $parent.height() - 5){
          $parent.data("stop",false);
          scrollView($parent,1);
        }else{
          $parent.data("stop",true);
        }

        $node.offset({top:top});
        if(!$parent.hasClass("notSelect")){
          $parent.addClass("notSelect");
        }

        $parent.find(".amx-listItem:not(.amx-listItem-drop-spacer,.move)").each(function(){
          var $item = $(this);
          var itemOffset = $item.offset();
          var nodeOffset = $node.offset();
          var height = $item.height();
          if(itemOffset.top <= nodeOffset.top && nodeOffset.top <= itemOffset.top + height){
            if(nodeOffset.top <= itemOffset.top + height/2){
              $dropSpace.insertBefore($item);
            }else{
              $dropSpace.insertAfter($item);
            }
            return false;
          }
        });
      },
      end:function(event,dragExtra){
        var $clone = $node.clone();
        var $parent = $node.parent();
        $parent.append($clone);
        $node.hide();
        $clone.animate({opacity:0,height:0},function(){
          $clone.remove();
          $node.insertAfter($dropSpace);
          $node.show();
          $node.removeClass("move");
          $node.css("top","");
        });
        $dropSpace.animate({height:32},function(){
          $dropSpace.remove();
        });
        $parent.removeClass("notSelect");
        $parent.data("stop",true);
      }
    });
  }


  function switchToEditMode($listView) {
    $listView.addClass("amx-listView-editMode");
    $listView.find(".amx-listItem").each(function() {
      var $listItem = $(this);
      $listItem.append("<div class='amx-listItem-handle'></div>");
      handleMove($listItem);
    });

  }

  function switchToNormalMode($listView) {
    $listView.removeClass("amx-listView-editMode")
    $listView.find(".amx-listItem-handle").remove();
  }

  function scrollView($scrollable,direction){
    direction = direction == 1 ? 1 : -1;
    var stop = amx.isValueTrue($scrollable.data("stop"));
    $scrollable.scrollTop($scrollable.scrollTop() + (direction * 5));
    if(!stop){
      setTimeout(function(){
        scrollView($scrollable,direction);
      },300);
    }
  }
  // add this renderer
  amx.registerRenderers("amx",amxRenderers);

})();

