//
// 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 TreeNode = function( id, radius, arc, x, y ) {
	    this['rootPos'] = parseInt(Math.random()*360);

        this['parent'];
		this['branch_weight']=0;

        this['weight']=1;

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

        this['arc']=arc;

        this['target']=new Object();
        this['target']['x']=x;
        this['target']['y']=y;

        this['position']=new Object();
        this['position']['x'] = x;
        this['position']['y'] = y;

        this['force']=new Object();
        this['force']['x'] = 0;
        this['force']['y'] = 0;

        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'] + 'px';
		this['domNode'].style.top = this['position']['y'] + '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;
          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['arc']/(this['children'].length)*i) + this['rootPos'] - (this['arc']/(this['children'].length)*(this['children'].length-1))/2;
			}
            var node = this['children'][i];

			node['rootPos'] = parseInt(angle);
			
			var stagger = 0;
			if ( i % 2 ) { stagger = 30; }
			
	        node['target']['y'] = this['target']['y'] - 
			                      (Math.cos(angle*(Math.PI/180)) * 
								  (this['radius']+stagger)) * node['weight'];
		    node['target']['x'] = this['target']['x'] - 
			                      ((Math.sin(angle*(Math.PI/180)) * 
								  (this['radius']+stagger)))*graph['skew'] * node['weight'];
			
            var domNode = this['children'][i]['domNode'];
 //           domNode.style.top = node['position']['y'];// + parseInt(node['domNode']['offsetHeight']/2) + 'px';
//            domNode.style.left = node['position']['x'] + 100;// + parseInt(node['domNode']['offsetWidth']/2) + 'px';

            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'];
		
		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'] );

            this.attractiveForce( node, node['children'][i], distance );
            node['children'][i]['position']['x'] += node['children'][i]['force']['x'];
            node['children'][i]['position']['y'] += node['children'][i]['force']['y'];

            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';

            node['children'][i]['force']['x'] = 0;
            node['children'][i]['force']['y'] = 0;

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

		// apply an attractive force between two nodes
	    this.attractiveForce = function( nodeI, nodeJ, distance ) {
          var weight=2;
          var attractive_force = (distance['d'] - weight)/weight;
		  if ( distance['dx'] ) {
	        nodeJ['force']['x'] += attractive_force * distance['dx'] / distance['d'];
          }
	      if ( distance['dy'] ) {
	        nodeJ['force']['y'] += attractive_force * distance['dy'] / distance['d'];
          }
	    };

		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
		  // k+factor at end determines dot frequency
		  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/>";
