//
// This work is licensed under the Creative Commons Attribution 2.5 License. To 
// view a copy of this license, visit
// http://creativecommons.org/licenses/by/2.5/
// or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San
// Francisco, California, 94105, USA.
//
// All copies and derivatives of this source must contain the license statement 
// above and the following attribution:
//
// Author: Kyle Scholz      http://kylescholz.com/
// Copyright: 2006
//

var doStagger = false;

      var TreeNode = function( id, radius, fan_angle, x, y ) {
	    this['rootPos'] = parseInt(Math.random()*360);

        // each node knows it's parent so we can simplify calcuation of "branch weight"
        this['parent'];

        // branch weight represents the weight of all of this node's children
		this['branch_weight']=0;

        this['weight']=1;

        this['id'] = id;
        this['radius'] = radius;

        this['fan_angle']=fan_angle;

        this['target']=new Object();
        this['target']['t'] = 0;
        this['target']['r'] = 0;
				
        this['position']=new Object();
        this['position']['x'] = x;
        this['position']['y'] = y;
        this['position']['t'] = 0;
        this['position']['r'] = 0;
		
		// TODO: move to domui.js
        this['domNode'] = document.createElement( "div" );
        this['domNode'].style.position = "absolute";
        this['domNode'].innerHTML = '<img src="http://www.kylescholz.com/cgi-bin/bubble.pl?title=&r=12&pt=8&b=656565&c=99ee55">';
        this['domNode'].style.left = (this['position']['x'] - parseInt(this['domNode']['offsetWidth']/2) + graph.frame_left) + 'px';
        this['domNode'].style.top = (this['position']['y'] - parseInt(this['domNode']['offsetHeight']/2) + graph.frame_top) + 'px';

		this['domNode'].style.zIndex = 10;
        document.body.appendChild( this['domNode'] );
				
        this['children'] = new Array();

		/**
		 * 
		 */
        this.incrementBranchWeight = function() {
          this['branch_weight']++;
		  if ( this['parent'] ) {
		    this['parent'].incrementBranchWeight();
	      }
		};
		
		/**
		 * 
		 */
        this.addChild = function( node ) {
          this['children'].push(node);
          node['parent'] = this;
          node['position']['t'] = this['rootPos'];
          this.incrementBranchWeight();
          this.updateChildren();
		  return node;
		};
		
		/**
		 * 
		 */
		this.updateChildren = function() {
          for ( var i=0; i<this['children'].length; i++ ) {
            var angle;
			if ( this['children'].length == 1 ) {
              angle=this['rootPos'];
            } else {
              angle=(this['fan_angle']/(this['children'].length)*i) + this['rootPos'] - (this['fan_angle']/(this['children'].length)*(this['children'].length-1))/2;
			}
            var node = this['children'][i];

			node['rootPos'] = parseInt(angle);

	        node['target']['t'] = angle;
	        node['target']['r'] = this['radius'];

            var domNode = this['children'][i]['domNode'];

            node.updateChildren();
	      }
		};
      }

	  // Distance:
	  var Distance = function(){};
	  Distance.prototype = {
	    calculate: function( pointA, pointB ) {
	      // X Distance
	      this['dx'] = pointA['x'] - pointB['x'];
	      // Y Distance
	      this['dy'] = pointA['y'] - pointB['y'];
	      this['d2'] = (this['dx']*this['dx']+this['dy']*this['dy']);
	      // Distance
	      this['d'] = Math.sqrt(this['d2']);
	    }
	  };

      var TreeGraph = function( frame_width, frame_height, frame_top, frame_left ) {

	    this['frame_width']=frame_width;
        this['frame_height']=frame_height;

	    this['frame_top']=frame_top;
        this['frame_left']=frame_left;
		
        this['skew']=frame_width/frame_height;
		
	    this['origin'];

		// stats for measuring entropy
		this['countNodesMoved']=0;
		this['maxTargetGap']=0;
		this['totalTargetGap']=0;
		
		/**
		 * 
		 */			
		this.setOrigin = function( origin ) {
		  this['origin'] = origin;
		};
		
		/**
		 * 
		 */
        this.update = function() {
          this.setChildrenForces( this['origin'] );
        };
		
		/**
		 * 
		 */
		this.setChildrenForces = function( node ) {
          for( var i=0; i<node['children'].length; i++ ) {
//            var distance = new Distance();
 // 		    distance.calculate( node['children'][i]['target'], node['children'][i]['position'] );

            var fluidity = 3;
			var newAngle = node['children'][i]['position']['t'] -
  			  ((node['children'][i]['position']['t'] - node['children'][i]['target']['t']) / fluidity);
            var newRadius = node['children'][i]['position']['r'] -
  			  ((node['children'][i]['position']['r'] - node['children'][i]['target']['r']) / fluidity);
			  			  
            node['children'][i]['position']['t'] = newAngle;
            node['children'][i]['position']['r'] = newRadius;
			
			var stagger = 0;
			if ( doStagger && i % 2 ) { stagger = 30; }
			
	        node['children'][i]['position']['y'] = node['position']['y'] - 
			                      (Math.cos(newAngle*(Math.PI/180)) * 
								  (node['children'][i]['position']['r']+stagger)) * node['children'][i]['weight'];
		    node['children'][i]['position']['x'] = node['position']['x'] - 
			                      ((Math.sin(newAngle*(Math.PI/180)) * 
								  (node['children'][i]['position']['r']+stagger)))*graph['skew'] * node['children'][i]['weight'];


            node['children'][i]['domNode'].style.left = (node['children'][i]['position']['x'] - parseInt(node['children'][i]['domNode']['offsetWidth']/2) + frame_left) + 'px';
            node['children'][i]['domNode'].style.top = (node['children'][i]['position']['y'] - parseInt(node['children'][i]['domNode']['offsetHeight']/2) + frame_top) + 'px';

            this.setChildrenForces( node['children'][i] );
			this.drawEdge( node, node['children'][i] );
		  }
		};

		/**
		 * 
		 */
		this.drawEdge = function ( nodeI, nodeJ ) {

		  // edges should appear between center of nodes
		  var centeri = new Object();
		  centeri['x'] = nodeI['position']['x'];
		  centeri['y'] = nodeI['position']['y'];
		
		  var centerj = new Object();
		  centerj['x'] = nodeJ['position']['x'];
		  centerj['y'] = nodeJ['position']['y'];
		
		  // get a distance vector between nodes
		  var distance = new Distance();
		  distance.calculate( centeri, centerj );

		  // draw line
		  var l = 8;
		  for ( var k=1; k<l; k++ ) {
		    var p = (distance['d'] / l) * k;
		    var pix;
		
		    try {
		      // dom updates are expensive ... recycle where we can
		      if ( !document.getElementById( 'edge' + nodeI.id + ':' + nodeJ.id ) ) {
                var edge = document.createElement("div");
                edge.id = 'edge'+nodeI.id+':'+nodeJ.id;
                document.body.appendChild(edge);
			  }
			  
		      if ( !document.getElementById('edge' + nodeI.id + ':' + nodeJ.id + ':' + k) ) {
		        pix = pixTmpl.cloneNode(true);
		        pix.id = 'edge' + nodeI.id + ':' + nodeJ.id + ':' + k;
		        document.getElementById('edge' + nodeI.id + ':' + nodeJ.id).appendChild(pix);
		      } else {
		        pix = document.getElementById('edge' + nodeI.id + ':' + nodeJ.id + ':' + k);
		      }
		      pix.style.left=centeri['x'] +(-1)*p*(distance['dx']/distance['d']) + frame_left + 'px';
		      pix.style.top=centeri['y'] +(-1)*p*(distance['dy']/distance['d']) + frame_top + 'px';
		    } catch ( e ) {
		
		    }
		  }
		};
		
      }

// Edge Point Template
var pixTmpl = document.createElement( 'div' );
pixTmpl.style.width = '2px';
pixTmpl.style.height = '2px';
pixTmpl.style.backgroundColor = '#888888';
pixTmpl.style.position = 'absolute';
pixTmpl.innerHTML="<img height=1 width=1/>";

