// Border Line - draw lines with borders


function Point(x,y) {
  this.x=x;
  this.y=y;
  this.moveBy=function(dx,dy){
    this.x+=dx;
    this.y+=dy;
    this.onchange();
  };

  this.moveTo=function(x,y){
    this.x=x;
    this.y=y;
    this.onchange();
  };

  this.onchangeCallBack=[];
  this.addChangeHandler = function(f){
    this.onchangeCallBack.push(f);
  };
  this.removeChangeHandler = function(f){
    var i,j;
    for(i=0,j=0;i<this.onchangeCallBack.length;i++) {
      if (this.onchangeCallBack[i]==f) {
	this.onchangeCallBack[j++]=this.onchangeCallBack[i]
      }
    }
    for(;j<i;j++) this.onchangeCallBack[j]=undefined;
  };

  this.onchange=function () {
    
    for (var i in this.onchangeCallBack) 
      { 
	this.onchangeCallBack[i]();
      }
  }

}


function Line(p1,p2) {
  var POINT=0,VERTICAL=1,HORIZONTAL=2,UP=3,DOWN=4;

  this.POINT=POINT;
  this.VERTICAL=VERTICAL;
  this.HORIZONTAL=HORIZONTAL;
  this.UP=UP;
  this.DOWN=DOWN;
  this.p1=p1; 
  this.p2=p2;
  this.calculate= function  () {
    this.xmax = Math.max(this.p1.x,this.p2.x);
    this.xmin = Math.min(this.p1.x,this.p2.x);
    this.ymax = Math.max(this.p1.y,this.p2.y);
    this.ymin = Math.min(this.p1.y,this.p2.y);
    this.width = Math.abs(this.xmax-this.xmin);
    this.height = Math.abs(this.ymax-this.ymin);
    this.dxy = this.width/this.height;
    if (this.p1.x == this.p2.x) {
      if (this.p1.y == this.p2.y) 
	this.slant=this.POINT;
      else
	this.slant=this.VERTICAL;
    } else if (this.p1.y==this.p2.y) {
      this.slant=this.HORIZONTAL;
    } else if ((this.p2.x-this.p1.x)*(this.p2.y-this.p1.y)>0) {
      this.slant=this.DOWN;
    } else {
      this.slant=this.UP;
    }
  };
  var self=this; // save this, keyword this is fully dynamically scoped.
  var recalc = function () {self.calculate();}
  p1.addChangeHandler(recalc);
  p2.addChangeHandler(recalc);
  this.calculate();

  this.pointDistance = function(p) {
    // Normalize as if p1 was origo.
    var dy=this.p2.y-this.p1.y;
    var dx=this.p2.x-this.p1.x;
    var px=p.x-this.p1.x;
    var py=p.y-this.p1.y;
    return (-dy*px+dx*py)/Math.sqrt(dx*dx+dy*dy);
  }

  // would drawing this line cover the point p
  this.covers=function(p) {
    if (p.x<=this.xmin || p.x >= this.xmax || p.y <= this.ymin || p.y >= this.ymax) return false;
    if (this.slant==this.DOWN) {
       if ((p.x-this.xmin)/(this.width/this.height)+this.ymin<=p.y) return true;
    } else {
       if ((this.xmax-p.x)/(this.width/this.height)+this.ymin>=p.y) return true;
    }
    return false; 
  }
 
  this.obscures=function(l) {
    if (this.slant<=this.HORIZONTAL) return false; // lines obscures nothing
    if (this.covers(l.p1) || this.covers(l.p2)) return true;
    if (l.xmax<=this.xmin || l.xmin>=this.xmax || l.ymax<=this.ymin || l.ymin>=this.ymax) 
      return false;
    var yline = this.slant==this.DOWN?this.ymax:this.ymin;
    // find where extension of l crosses lines x=xmin and y=(slant==UP?ymin:ymax)
    var dx = l.p2.x-l.p1.x;
    var dy = l.p2.y-l.p1.y;

    // solves (y1+dy*t)=yline wrt t, i.e., t where extension of l crosses horizontal line of triangle
    var tx = (yline-l.p1.y)/dy; 
    var x0 = l.p1.x + tx * dx; // x where extended line crosses horizontal line
    if (x0 > this.xmin && x0 < this.xmax && tx >0 && tx <1) return true;

    var ty = (this.xmin-l.p1.x)/dx;
    var y0 = l.p1.y + ty * dy;
    if (y0 > this.ymin && y0< this.ymax && ty > 0 && ty < 1) return true;
    
    return false;
  }


  // foreground element, background elemen (both divs with transparent 
  // l/r borders and background-colorl
  // ofx, ofy : offset for positioning, if coordinates are relative
  // fgc, bgc : colors, otherwise assumed already set on elements

  this.draw=function(fg,bg,ofx,ofy,fgc,bgc,bstyle) {

    var fgs=fg.style;
    var bgs=bg.style;
    if (!ofx)ofx=0;
    if (!ofy)ofy=0;
    if (fgc) {
        fgs.borderTopColor = fgc;
        fgs.borderBottomColor = fgc;
        fgs.borderLeftColor = "transparent";
        fgs.borderRightColor = "transparent";
	}
    if (bgc) {
        bgs.borderTopColor = bgc;
        bgs.borderBottomColor = bgc;
        bgs.borderLeftColor = "transparent";
        bgs.borderRightColor = "transparent";
	}
    if (bstyle) {fgs.borderStyle=bstyle;bgs.borderStyle.bstyle;}

    var dxy =(this.slant<=this.HORIZONTAL?0:Math.ceil(this.dxy))
    fgs.left=(this.xmin+ofx)+"px"
    fgs.top =(this.ymin+ofy)+"px"
    
    if (this.slant <= this.HORIZONTAL) {
      var borders;
      if (this.slant<=this.VERTICAL) {
	borders = Math.max(1,this.height) +"px 0px 0px 0px";
	fgs.width="1px";
	fgs.height="0px";
      } else {
	borders = "1px 0px 0px 0px";
	fgs.width = this.width +"px";
	fgs.height="0px";
      }
      fgs.borderWidth=borders;
      bgs.visibility="hidden";
    } else {
      var fborders;
      var bborders;
      var btop;
      fgs.backgroundColor="trasparent";
      fgs.width="0px";
      fgs.height="0px";
      if (this.slant==this.UP) {
	fborders = this.height+"px "+this.width+"px 0px 0px";
	bborders = (this.height-Math.ceil(1/this.dxy))+"px "+(this.width-Math.ceil(this.dxy))+"px 0px 0px";
	btop=0;
      } else {
	fborders = "0px "+this.width+"px "+this.height+"px 0px";
	bborders = "0px "+(this.width-Math.ceil(this.dxy))+"px "+(this.height-Math.ceil(1/this.dxy))+"px 0px";
	btop = Math.ceil(1/this.dxy);
      }
      var dxy = Math.ceil(this.dxy);
      fgs.borderWidth = fborders;
      bgs.borderWidth = bborders;
      bgs.left=(this.xmin+ofx)+"px"
      bgs.top=(this.ymin+ofy+btop)+"px"
      bgs.visibility="visible";
    }

  }
}
    
function LineSet() {
  this.lines = new Array();
  this.addLine=function(l){this.lines.push(l);};

  
  this.sortLines = function(){
    var newlines = new Array();
    var matrix = new Array();
    var cnt = new Array();
    for(var i=0;i<this.lines.length;i++) {
      matrix[i]=new Array();
      cnt[i]=0;
      for(var j=0;j<this.lines.length;j++)
	if (i==j) 
	  matrix[i][j]=false;
        else {
          if (this.lines[j].obscures(this.lines[i]) && (j>i || !matrix[j][i])) {
	    matrix[i][j]=true;
	    cnt[i]++;
          } else {
	    matrix[i][j]=false;
  	  }
	}
    }
      for (var i=0;i<this.lines.length;i++) {
        for (var j=0;j<this.lines.length;j++) {
	  if (cnt[j]==0) {
	    newlines.push(this.lines[j]);
	    cnt[j]=-1;
	    for(var k=0;k<this.lines.length;k++)
	      {if (matrix[k][j]) cnt[k]--;}
	    break;
	  }
        }
        if (j==this.lines.length) { // WARNING, LINES CROSSES, CYCLIC DEPENDENCY!
          return; // Do nothing, it's safer that way.    
        }
      }
    this.lines=newlines;
    };

  this.draw=function(elems){
    this.sortLines();
    for(var i=0;i<this.lines.length && i*2+1<elems.length;i++) {
      this.lines[i].draw(elems[i*2],elems[i*2+1]);
    }
  };
}    
