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

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

  //a two-dimensional vector class to aid in animation; necessary for animating along axes that are
  //not aligned perfectly with X or Y axes; also aids in avoiding unecessary sqrt() operations by
  //enabling lazy calculation of the vector magnitude
  function Vector2D()
  {
    this.x = 0.0;
    this.setX = setX;
    this.getX = getX;
    
    this.y = 0.0;
    this.setY = setY;
    this.getY = getY;
    
    this.magnitude = 0.0;
    this.magnitudeValid = true;
    this.setMagnitude = setMagnitude;
    this.getMagnitude = getMagnitude;
    
    this.magnitude2 = 0.0;
    this.magnitude2Valid = true;
    this.getMagnitude2 = getMagnitude2;
  }
  
  //set the X component of the vector
  function setX(nx)
  {
    this.x = nx;
    this.magnitudeValid = false;
    this.magnitude2Valid = false;
  }
  
  //get the X component of the vector
  function getX()
  {
    return this.x;
  }
  
  //set the Y component of the vector
  function setY(ny)
  {
    this.y = ny;
    this.magnitudeValid = false;
    this.magnitude2Valid = false;
  }
  
  //get the Y component of the vector
  function getY()
  {
    return this.y;
  }
  
  //changes the magnitude of the vector, which will also affect the X and Y values
  function setMagnitude(nm)
  {
    var that = this;
    
    if (nm == 0.0)
    {
      //avoid the additional sqrt calculation
      that.magnitude = 0.0;
      that.magnitude2 = 0.0;
      that.x = that.y = 0.0;
      that.magnitudeValid = true;
      that.magnitude2Valid = true;
    }
    else
    {
      //recalculate X & Y by first converting to a unit vector using the previous magnitude
      var oldMagnitude = that.getMagnitude();
      that.magnitude = Math.abs(nm);
      that.x = (that.x/oldMagnitude) * that.magnitude;
      that.y = (that.y/oldMagnitude) * that.magnitude;
      that.magnitudeValid = true;
      that.magnitude2Valid = false;
    }
  }
  
  //gets the magnitude of the vector based on the X and Y coordinates; lazily calculate the magnitude
  //when requested to avoid unnecessary invocations of sqrt()
  function getMagnitude()
  {
    var that = this;
    if (!that.magnitudeValid)
    {
      that.magnitude = Math.sqrt(that.getMagnitude2());
      that.magnitudeValid = true;
    }
    
    return this.magnitude;
  }
  
  //calculates the square magnitude of the vector, useful for performing collision tests and magnitude
  //comparisons without incurring the cost of constant invocations of sqrt()
  function getMagnitude2()
  {
    var that = this;
    if (!that.magnitude2Valid)
    {
      if (that.magnitudeValid)
      {
        that.magnitude2 = that.magnitude*that.magnitude;
      }
      else
      {
        that.magnitude2 = (that.x*that.x) + (that.y*that.y);
      }
      that.magnitude2Valid = true;
    }

    return this.magnitude2;
  }
  
  //the constant amount of drag against momentum (pixels/millisecond)
  var VELOCITY_DRAG = 0.002;
  
  //the time delta of events over which the momentum resulting from a drag is calculated (milliseconds)
  var VELOCITY_TIME_THRESHOLD = 200;

  var currentDragElement = null;
  var touches = [];

  $(function(){

    $(document).drag(".amx-scrollable", {
      start: function(event,dragExtra)
      {
          if (currentDragElement != null)
          {
            //a new drag has started; stop the current momentum animation
            currentDragElement.momentum.setMagnitude(0.0);
            currentDragElement = null;
          }
      },

      drag: function(event,dragExtra)
      {
        //only scroll components whose scrollable area is larger than their viewable area
        if (!dragExtra || typeof(dragExtra) == "undefined" || (currentDragElement != null && currentDragElement != event.currentTarget) ||
            (event.currentTarget.scrollHeight <= $(event.currentTarget).innerHeight() && event.currentTarget.scrollWidth <= $(event.currentTarget).innerWidth()))
        {
          return;
        }

        //scroll the compnent in the direction of the drag; keep track of the events caused by dragging to allow us
        //to later calculate the moment when the drag is released
        currentDragElement = event.currentTarget;
        currentDragElement.scrollLeft -= dragExtra.deltaPageY;
        currentDragElement.scrollTop -= dragExtra.deltaPageY;
        touches.push(dragExtra);
      },

      end: function(event,dragExtra)
      {
        if (currentDragElement == null || currentDragElement != event.currentTarget)
        {
          //this is not the end drag event for our target component
          return;
        }
        
        //find the range of events over the last X milliseconds
        var i = 0;
        while (i < touches.length && event.timeStamp - touches[i].eventSource.timeStamp > VELOCITY_TIME_THRESHOLD)
        {
          i++;
        }
        if (i == touches.length)
        {
          //there were no events that occurred within our maximum time slice; user stopped dragging before
          //releasing the touch; no momentum
          touches = [];
          return;
        }

        //determine the actual amount of time covered by the events that occurred within our maximum time slice
        var dragTime = touches[touches.length-1].eventSource.timeStamp - touches[i].eventSource.timeStamp;
        if (dragTime == 0)
        {
          //user stopped dragging before releasing the touch; thus no momentum is necessary
          touches = [];
          return;
        }

        //the momentum is equal to the number of pixels move during the last X milliseconds
        currentDragElement.momentum = new Vector2D();
        currentDragElement.momentum.setX((touches[touches.length-1].pageX - touches[i].pageX) / dragTime);
        currentDragElement.momentum.setY((touches[touches.length-1].pageY - touches[i].pageY) / dragTime);
        touches = [];
        currentDragElement.lastAnimationTime = event.timeStamp;
        
        var doMomentum = function()
        {
          if (currentDragElement == null)
          {
            //animation stopped
            return;
          }
          var now = Date.now();
          var deltaTime = now - currentDragElement.lastAnimationTime;
          if (deltaTime == 0)
          {
            //no time occurred between the last drag event and the touch release
            return;
          }
          
          //update the position
          currentDragElement.scrollLeft -= currentDragElement.momentum.getX() * deltaTime;
          currentDragElement.scrollTop -= currentDragElement.momentum.getY() * deltaTime;
          currentDragElement.lastAnimationTime = now;
          
          //update the velocity
          var deltaVelocityDrag = VELOCITY_DRAG * deltaTime;
          if (currentDragElement.momentum.getMagnitude2() <= deltaVelocityDrag*deltaVelocityDrag)
          {
            //component has slowed to a stop; done animating
            currentDragElement.momentum.setMagnitude(0.0);
            currentDragElement = null;
          }
          
          //further slow the component; continue animating
          currentDragElement.momentum.setMagnitude(currentDragElement.momentum.getMagnitude()-deltaVelocityDrag);
          setTimeout(doMomentum, 17);
        };
        
        doMomentum();
      }
      
    });
  });

})();

(function() {
  var amxRenderers = {

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

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

    },

 
     spacer: function(amxNode){
         var width = amx.getTextValue(amxNode.width);
         var height = amx.getTextValue(amxNode.height);
         var hidden = !amx.isNodeRendered(amxNode);
         
         if ((hidden) || (width == null) || (width.length <= 0))
         {
             if ((hidden) || (height == null) || (height.length < 0))
             {
                 // Both width and height are null or zero, just render an empty span.
                 return $("<span></span>");
             }
             else // it has a height but not a width
             {
                 if (height.length != 0)
                 {
                     // our default unit is px
                     return $("<div style='margin-top:" + height + "px'>" + "</div>");
                 }
                 else
                 {
                     return $("<div></div>");
                 }
             }
         }
         else // it at least has a width (it might have a height)
         {
             if ((height == null) || (height.length <= 0))
             {
                height = 0;
             }
             
             return $("<div style='display-inline:block;margin-top:" + height + "px;margin-right:" + width + "px'>" + "</div>");
         }
     },

 

    verbatim: function(amxNode){
      var $node = $("<div></div>");
      var content = amxNode._xmlNode.textContent;
      $node.html(content);
      return $node;
    },

    goLink: function(amxNode){
      var $node = $("<a href='" + amx.getTextValue(amxNode.url) + "'>" + amx.getTextValue(amxNode.text) + "</a>");
      
      //render child elements if there are any
      return amx.renderSubNodes(amxNode,$node).pipe(function()
      {
        return $node;
      });
    },

    outputText: function(amxNode)
    {
      var displayValue = amx.getTextValue(amxNode.value);
      var $node = $("<span></span>");
      $node.text(displayValue);

      var truncateAt = parseInt(amxNode.truncateAt);
      if (!isNaN(truncateAt) && truncateAt > 0 && typeof amxNode.value !="undefined")
      {
        // from the tagdoc:
        // the length at which the text should automatically begin truncating.
        // When set to zero (the default), the string will never truncate. Values
        // from one to fifteen will display the first 12 characters followed by an
        // ellipsis (...). The outputText component will not truncate strings shorter
        // than fifteen characters. For example, for the value of 1234567890123456,
        // setting truncateAt to 0 or 16 will not truncate. Setting truncateAt to any
        // value between 1-15 will truncate to 123456789012...
        if (truncateAt < 15)
        {
          truncateAt = 15;
        }

        $node.attr("amx-data-value",displayValue);
        if (truncateAt < displayValue.length)
        {
          displayValue = displayValue.substring(0,truncateAt - 3)+"...";
        }
        $node.text(displayValue);
        $node.addClass("amx-outputText-truncateAt");
      }
      return $node;
    },

    inputText: function(amxNode) {
      //var html = '<div data-role="fieldcontain" class="amx-fieldcontain"></div>';
      amx.registerInputValue(amxNode, "value");
      var field = amx.createField(amxNode);

      var $input;
      var oldValue;
      var $wrap = $("<div class='wrap'></div>");
      field.$value.append($wrap);

      if(amxNode.rows) {
        $input = $('<textarea name="'+amxNode.name+'" rows="'+amxNode.rows+'"></textarea>');
      } else {
        if(amx.isValueTrue(amxNode.secret)) {
          $input = $('<input type="password" name="'+ amxNode.name + '" />');
        } else {
          $input = $('<input type="text" name="' + amxNode.name + '" />');
        }
      }
      $wrap.append($input);

      if(amxNode.maximumLength) {
        $input.keypress( function() {
          if($input.val().length >= amxNode.maximumLength) {
            return false;
          }
        });
      }

      if(amxNode.value) {
        $input.val(amx.getTextValue(amxNode.value));
      }else{
        $input.val(amx.getTextValue(amxNode.hintText));
        $input.addClass("amx-hint");
      }
      oldValue = $input.val();

      if(amx.isValueTrue(amxNode.readOnly)) {
        $input.remove();
        $input = $('<div class="readOnlyLabel">'+amx.getTextValue(amxNode.value)+'</div>');
        $wrap.append($input);
      }

      if(amx.isValueTrue(amxNode.disabled)) {
        $input.attr("disabled",true);
      }

      $input.blur( function() {
        var value = $input.val();
        if(value == ""){
          $input.val(amx.getTextValue(amxNode.hintText));
          $input.addClass("amx-hint");
        }
        // set the amxNode value so that it stays in sync
        amxNode.value = value;
        if (oldValue !== value)
        {
          var vce = new amx.ValueChangeEvent(oldValue, value);
          amx.processAmxEvent(field.$node,"valueChange","value",value, vce);
        }
        else
        {
          amx.processAmxEvent(field.$node,"valueChange","value",value);
        }
      });

      $input.bind("focus",function(){
        if($input.hasClass("amx-hint")){
          $input.val("");
          $input.removeClass("amx-hint");
        }
        oldValue = $input.val();
      });
	  // calls applyRequiredMarker in amx-core.css to determine and implement required/showRequired style
      adf.mf.api.amx.applyRequiredMarker(amxNode, field);

      // call applyRequiredMarker in amx-core.css to determine and implement required/showRequired style
      adf.mf.api.amx.applyRequiredMarker(amxNode, field);

      // call applyRequiredMarker in amx-core.css to determine and implement required/showRequired style
      adf.mf.api.amx.applyRequiredMarker(amxNode, field);

      return field.$node;
    },

    image: function(amxNode) {
      var source = amx.getTextValue(amxNode.source);
      var html = '<img src="' + amx.buildRelativePath(source) + '">';
      return $(html);
    },

    commandLink: {
      create: function(amxNode) {
        var html = "<a></a>";
        var $node = $(html);

        // prevent the default behavior
        $node.bind('click',function(e){
          e.stopPropagation();
          e.preventDefault();
        });
        if (amx.isValueTrue(amxNode.disabled)) {
          $node.addClass("amx-disabled");
        } else if (amx.isValueTrue(amxNode.readOnly)) {
          $node.addClass("amx-readOnly");
        }

        $node.tap(function(event) {
          event.preventDefault();
          event.stopPropagation();
          if(!amx.isValueTrue(amxNode.disabled) && !amx.isValueTrue(amxNode.readOnly)){
            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);
                  }
                });
              }
            });
          }
        });

        if (amxNode.text){
          $node.append("<label>"+amx.getTextValue(amxNode.text)+"</label>");
        }

        amx.enableSwipe($node);
        amx.enableTapHold($node);

        if(amxNode._xmlNode && amxNode._xmlNode.firstElementChild) {
          return amx.renderSubNodes(amxNode,$node).pipe(function(){
            return $node;
          });
        } else {
          return $node;
        }
      },

      init: function($node,amxNode){
      }
    },

    commandButton: {
      create: function(amxNode) {
        //var html = '<a href data-role="button">' + amxNode.text + '</a>';
        var html = "<div><label class='amx-commandButton-label'>" + amx.getTextValue(amxNode.text) + "</label></div>";
        var $node = $(html);

        return $node;
      },

      init: function($node,amxNode) {

        if (amxNode._xmlNode.parentNode.tagName == "amx:facet")
        {
          if (amxNode.action == "__back")
          {
            if (amxNode.styleClass != "amx-commandButton-normal")
            {
              $node.addClass('amx-commandButton-back');
            }
          }
			//TODO: need to handle when parent is facet
        }

        if (amxNode.icon) {
           // if we have an '.', then assume it is an image
           if (amxNode.icon.indexOf(".") > -1) {
             $node.prepend("<img class='amx-commandButton-icon' src='" + amx.buildRelativePath(amxNode.icon) +"' />");
           }
           else {
             // otherwise, we assume it is a pre-built icon
             var $icon = $("<span class='amx-commandButton-icon'></span>");
             //$icon.css("background","url(images/test-icon.png)");
             //$node.prepend($node);
           }
            // Check for img icon position to be trailing or leading
           if (amxNode.iconPosition == "trailing") {
         	  $node.addClass("amx-iconPosition-trailing");
           }
           else {
         	  $node.addClass("amx-iconPosition-leading");
           }
        }
        	// Grabbing the label text of the commandButton
		  var commandButtonLabel = $node.find($('.amx-commandButton-label'));
          var commandButtonLabelText = commandButtonLabel.html();

          if (amx.isValueTrue(amxNode.disabled)) {
          	 $node.addClass("amx-disabled");
          }
          if (commandButtonLabelText == '') {
         	 $node.addClass("amx-label-no-text");
          }
        $node.tap(function(event) {
          event.preventDefault();
          event.stopPropagation();
          if (!amx.isValueTrue(amxNode.disabled)) {
            // adf.mf.internal.perf.start("Tap validation" );
            amx.validate($node).done(function() {
                // adf.mf.internal.perf.stop("Tap validation" );
                // adf.mf.internal.perf.start("Tap processing" );
              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);
                  }
                  // adf.mf.internal.perf.stop("Tap processing" );
                });
              }
       	    });
          }
        });

        if (!amx.isValueTrue(amxNode.disabled)) {
          var mousedown = "mousedown";
          var mouseup = "mouseup";
          if(amx.hasTouch()){
            mousedown = "touchstart";
            mouseup = "touchend";
          }
          $node.bind(mousedown,function(){
            $node.addClass("amx-selected");
          });
          $node.bind(mouseup,function(){
            $node.removeClass("amx-selected");
          });
          $node.bind("mouseout",function(){
            $node.removeClass("amx-selected");
          });
        }
      }

    }

  }; // /var amxRenderers

  // --------- AMX Helper Functions --------- //

  (function()
  {
    // for all the controls (i.e. field) this will create the
    // basic field, field-label, field-value structure
    amx.createField = function(amxNode)
    {
      var field = {};

      field.$node = $("<div class='field'></div>");
      field.isReadOnly = amx.isValueTrue(amxNode.readOnly);
      field.isDisable = amx.isValueTrue(amxNode.disabled);

      var simple = amx.isValueTrue(amxNode.simple);
      if (simple)
      {
        field.$node.addClass("amx-simple");
        field.$label = $("<div class='field-label'></div>");
      }
      else
      {
        field.$label = $("<div class='field-label'><label>" + amx.getTextValue(amxNode.label) + "</label></div>");
      }
      field.$node.append(field.$label);
      field.$value = $("<div class='field-value'></div>");
      field.$node.append(field.$value);
      return field;
    }
  })(jQuery);
  // --------- /AMX Helper Functions --------- //

  // add this renderer
  amx.registerRenderers("amx",amxRenderers);

})();

