# Copyright 2012 United States Government, as represented by the Secretary of Defense, Under
# Secretary of Defense (Personnel & Readiness).
# 
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
# 
#   http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions and limitations under
# the License.

## The component representation of a control behavior
## 
## @name control.vwf
## @namespace

---
properties:
  controlValue: 
    set: |
      if("animationTime" in this && this.positions) {
        for(var i=0; i<this.positions.length; i++) {
          if(value == this.positions[i].controlValue) {
            this.animationTime = this.positions[i].animationTime;
          }
          else if(i+1 < this.positions.length && value > this.positions[i].controlValue && value < this.positions[i+1].controlValue) {
            var animationRatio = (this.positions[i+1].animationTime - this.positions[i].animationTime) / (this.positions[i+1].controlValue - this.positions[i].controlValue);
            this.animationTime = value * animationRatio;
          }
        }
      }
      if(value !== this.controlValue) {
        this.controlValue = value;
        this.controlValueUpdated();
      }
    value: 0
  controlScale: 1
  controlDirection: 
    set: |
      switch(value) {
        case 1:
        case -1:
          this.direction = value;
          break;
      }
    value: 1
  controlPlaying: false
  controlMomentary: false
  linkedControls: []
  initialTransform: 
  controlDisabled: false
  animationRate: 1
methods:
  mouseInit: 
  animateControlToPosition:
    parameters:
      - position
  controlTick:
  processLinkedControls:
events:
  controlValueUpdated:
  pointerDown:
  pointerMove:
  pointerUp:
scripts:
- |
  this.initialize = function() {
    
    // Locate child nodes that extend or implement "http://vwf.example.com/control/position.vwf"
    // to identify themselves as control key positions.

    this.positions = this.find( "./element(*,'http://vwf.example.com/control/position.vwf')" );

    // Fill in missing `controlValue` properties, distributing evenly between the left and right
    // positions that define `controlValue`.

    // 1: [ - ] => [ 0 ]
    // 1: [ 0, - ] => [ 0, 1 ]
    // 1: [ -, 1 ] => [ 0, 1 ]
    // 1: [ 0, -, - ] => [ 0, 1/2, 1 ]
    // 1: [ -, -, 1 ] => [ 0, 1/2, 1 ]
    // 1: [ 0, - , -, 1 ] => [ 0, 1/3 , 2/3, 1 ]

    var leftValue, leftIndex;
    var rightValue, rightIndex = -Infinity;

    if ( this.positions.length > 0 ) {

      this.positions.sort(function(a, b) {
        return a.sequence - b.sequence;
      });
      
      if ( this.positions[0].controlValue === null ) {
        this.positions[0].controlValue = 0;
      }

      if ( this.positions[this.positions.length-1].controlValue === null ) {
        this.positions[this.positions.length-1].controlValue = 1;
      }

      this.positions.forEach( function( position, index ) {

        if ( position.controlValue !== null ) {

          leftValue = position.controlValue;
          leftIndex = index;

        } else {

          if ( index > rightIndex ) {
            for ( rightIndex = index + 1; rightIndex < this.positions.length; rightIndex++ ) {
              if ( ( rightValue = /* assignment! */ this.positions[rightIndex].controlValue ) !== null ) {
                break;
              }
            }
          }

          position.controlValue = leftValue + ( rightValue - leftValue ) *
            ( index - leftIndex )  / ( rightIndex - leftIndex );

        }

      }, this );
    }
  } //@ sourceURL=http://vwf.example.com/control.vwf/scripts~initialize

  // Sets up the mouse pointer information used for dragging.
  this.mouseInit = function() {
    this.input = {
      "pointerInfo": undefined,
      "pickInfo": undefined,
      "previous": {
        "pointerInfo": undefined,
        "pickInfo": undefined,        
      },
      update: function( pointerInfo, pickInfo ){
        if(!this.previous.pointerInfo) {
          this.previous.pointerInfo = this.pointerInfo;
          this.previous.pickInfo = this.pickInfo;
        }
        this.pointerInfo = pointerInfo;
        this.pickInfo = pickInfo;
      }, 
      clear: function(){
        this.previous.pointerInfo = undefined;
        this.previous.pickInfo = undefined;
        this.pointerInfo = undefined;
        this.pickInfo = undefined;        
      },
      change: function() {
        var ret = [ 0, 0 ]
        if ( this.pointerInfo && this.previous.pointerInfo ) {
          ret[0] = this.pointerInfo.position[0] - this.previous.pointerInfo.position[0];
          ret[1] = this.pointerInfo.position[1] - this.previous.pointerInfo.position[1];
        }
        return ret;
      }
    };
  }
  
  this.pointerDown = function( pointerInfo, pickInfo ) {
    if(!this.controlDisabled) {
      if ( !this.input ) this.mouseInit();
      this.input.clear();
      this.input.pointerInfo = pointerInfo;
      this.input.pickInfo = pickInfo;
      this.positions = this.find( "./element(*,'http://vwf.example.com/control/position.vwf')" );
      this.positions.sort(function(a, b) {
        return a.sequence - b.sequence;
      });
      if(this.controlMomentary) {
        this.animateControlToPosition(this.positions[this.positions.length-1]);
      }
    }
  }

  this.pointerUp = function( pointerInfo, pickInfo ) {
    if(!this.controlDisabled) {
      if(this.controlMomentary) {
        this.animateControlToPosition(this.positions[0]);
      }
      else {
        for(var i=0; i<this.positions.length; i++) {
          if(this.input.previous.pointerInfo == undefined) {
            if(this.controlValue == this.positions[i].controlValue) {
              if(!((i + this.controlDirection) >= 0 && (i + this.controlDirection) < this.positions.length)) {
                this.controlDirection = this.controlDirection * -1;
              }
              this.animateControlToPosition(this.positions[i + this.controlDirection]);
            }
            else if(i+1 < this.positions.length && this.controlValue > this.positions[i].controlValue && this.controlValue < this.positions[i+1].controlValue) {
              if(this.controlDirection == 1) {
                this.animateControlToPosition(this.positions[i + 1]);
              }
              else {
                this.animateControlToPosition(this.positions[i]);
              }
            }
          }
          else {
            if(i+1 < this.positions.length && this.controlValue > this.positions[i].controlValue && this.controlValue < this.positions[i+1].controlValue) {
              var nearPosition, farPosition;
              if(Math.abs((this.positions[i].controlValue - this.controlValue)) < Math.abs((this.positions[i+1].controlValue - this.controlValue))) {
                nearPosition = this.positions[i];
                farPosition = this.positions[i+1];
              }
              else {
                nearPosition = this.positions[i+1];
                farPosition = this.positions[i];
              }
              switch(nearPosition.controlType) {
                case 1:
                  this.animateControlToPosition(nearPosition);
                  break;
                case -1:
                  if(farPosition.controlType != -1) {
                    this.animateControlToPosition(farPosition);
                  }
              }
              break;
            }
          }
        }
      }
      this.processLinkedControls();
      this.input.clear();
    }
  }

  this.pointerMove = function( pointerInfo, pickInfo ) {
    if( !this.controlDisabled && !this.controlMomentary ) {
      if ( this.positions.length ) {
        var minValue = this.positions[ 0 ].controlValue;
        var maxValue = this.positions[ this.positions.length - 1 ].controlValue;
        this.input.update( pointerInfo, pickInfo );
        var diff = this.input.change();
        if(Math.abs(diff[0]) >= Math.abs(diff[1])) {
          var changeBy = diff[0] * this.controlScale;
          if(diff[0] > 0 && this.controlValue < maxValue) {
            this.controlDirection = 1;
            if((this.controlValue + changeBy) < maxValue) {
              this.controlValue = this.controlValue + changeBy;
            }
          }
          else if(diff[0] < 0 && this.controlValue > minValue) {
            this.controlDirection = -1;
            if((this.controlValue + changeBy) > minValue) {
              this.controlValue = this.controlValue + changeBy;
            }
          }
        }
        else {
          var changeBy = diff[1] * this.controlScale;
          if(diff[1] > 0 && this.controlValue < maxValue) {
            this.controlDirection = 1;
            if((this.controlValue + changeBy) < maxValue) {
              this.controlValue = this.controlValue + changeBy;
            }
          }
          else if(diff[1] < 0 && this.controlValue > minValue) {
            this.controlDirection = -1;
            if((this.controlValue + changeBy) > minValue) {
              this.controlValue = this.controlValue + changeBy;
            }
          }
        }
        this.processLinkedControls();
        this.input.previous.pointerInfo = pointerInfo;
        this.input.previous.pickInfo = pickInfo;
      }
    }
  }

  this.animateControlToPosition = function(position) {
    this.controlPlaying = true;
    this.controlDirection = this.controlValue < position.controlValue ? 1 : -1;
    this.controlTick(position);
  }

  this.controlTick = function (nextPosition) {
    if ( this.controlPlaying ) {
      if ( Math.abs(this.animationTime - nextPosition.animationTime) < goog.vec.EPSILON ) {
        this.controlValue = nextPosition.controlValue;
        if(nextPosition.controlType == -1) {
          for(var i=0; i<this.positions.length; i++) {
            if(this.positions[i].controlValue == nextPosition.controlValue) {
              if(i-1 >= 0 && this.positions[i-1].controlType != -1) {
                this.controlDirection = -1;
                nextPosition = this.positions[i-1];
              }
              else if(i+1 < this.positions.length && this.positions[i+1].controlType != -1) {
                this.controlDirection = 1;
                nextPosition = this.positions[i+1];
              }
              break;
            }
          }
        }
        else {
          this.controlPlaying = false;
        }
      }
      this.processLinkedControls();
      // Schedule the next tick if still playing.
      if ( this.controlPlaying ) {
        if ( Math.abs(nextPosition.animationTime - this.animationTime) > 1/60 ) {
          this.animationTime = this.animationTime + (1/60 * this.controlDirection * this.animationRate);
          this.in( 1/60 ).controlTick(nextPosition); // next interval
        } else {
          this.animationTime = nextPosition.animationTime;
          this.at( Math.abs(nextPosition.animationTime - this.animationTime) ).controlTick(nextPosition); // exactly at end
        }
      }
    }
  }

  this.processLinkedControls = function() {
    for(var i=0; i<this.linkedControls.length; i++) {
      var linkedControl = this.find("//" + this.linkedControls[i]);
      if(linkedControl.length > 0) {
        linkedControl[0].controlDirection = this.controlDirection;
        linkedControl[0].controlValue = this.controlValue;
        linkedControl[0].animationTime = this.animationTime;
      }
    }
  }