function Line( x1, y1, x2, y2, bIncludeStartNode )
{
    // set properties
    this.visible = false;
    this.x1 = x1;
    this.x2 = x2;
    this.y1 = y1;
    this.y2 = y2;
    this.numPoints = -1;     // override setting to force a set number of points
    this.bIncludeStartNode = bIncludeStartNode
    this.pointSpacing = 5;          // spaces between points
    this.layerNodeList = new Array();   // array of available node layers
    this.layerPointList = new Array();  // array of available point layers
    this.usedLayerNodeList = new Array();   // array of used node layers
    this.usedLayerPointList = new Array();  // array of used point layers    
    this.nodeWidth = 7;             // width of node image
    this.nodeHeight = 7;            // height of the node image
    this.pointWidth = 6;            // width of the regular point
    this.pointHeight = 6;           // height of the regular point
    
    // set methods
    this.drawLine = lineDraw;
    this.hideLine = lineHide;
    this.showLine = lineShow;
    this.deleteLine = lineDelete;
    this.intersectsWith = intersectsWith;
    
    // local methods
    this.drawNode = drawNode;
    this.drawPoints = drawPoints;
    this.drawPoint = drawPoint;
    
    function lineDraw()
    {   
        // draw the end node at the given position
        this.drawNode( this.x2, this.y2 );

        // draw the intermediate points on the line
        this.drawPoints( this.x1, this.y1, this.x2, this.y2, this.pointSpacing, 
                                                                this.numPoints );

        // draw the start node if necessary
        if ( this.bIncludeStartNode )
            this.drawNode( this.x1, this.y1 );

        // flag as visible
        this.visible = true;
    }
    
    function lineHide()
    {
        // init vars
        var szTmpLayer;
        
        // loop and hide all nodes
        var nCount = this.usedLayerNodeList.length;
        for ( var i=0; i < nCount; i++ )
        {
            // get next layer
            szTmpLayer = this.usedLayerNodeList[i];

            // hide layer
            CWCDHTML_SetLayerVis( szTmpLayer, false );
        }
        
        // loop and hide all points
        for ( i=0; i < this.usedLayerPointList.length; i++ )
        {
            // get next layer
            szTmpLayer = this.usedLayerPointList[i];
            
            // hide layer
            CWCDHTML_SetLayerVis( szTmpLayer, false );
        }
        
        // set property flag
        this.visible = false;
    }
    
    function lineShow()
    {
        // init vars
        var szTmpLayer;
        
        // loop and show all nodes
        for ( var i=0; i < this.usedLayerNodeList.length; i++ )
        {
            // get next layer
            szTmpLayer = this.usedLayerNodeList[i];

            // hide layer
            CWCDHTML_SetLayerVis( szTmpLayer, true );
        }
        
        // loop and show all points
        for ( i=0; i < this.usedLayerPointList.length; i++ )
        {
            // get next layer
            szTmpLayer = this.usedLayerPointList[i];
            
            // hide layer
            CWCDHTML_SetLayerVis( szTmpLayer, true );
        } 
        
        // set property flag
        this.visible = true;
    }
    
    function lineDelete()
    {
        // init vars
        var szTmpLayer;
        
        // loop and push all layer back onto each array
        var nCount = this.usedLayerNodeList.length;
        for ( var i=0; i < nCount; i++ )
        {
            // get next layer
            szTmpLayer = this.usedLayerNodeList.pop();

            // add it to the available list
            this.layerNodeList.push( szTmpLayer );
           
            // hide layer
            CWCDHTML_SetLayerVis( szTmpLayer, false );
        }
        
        // loop and push all layers back onto each array
        var nCount = this.usedLayerPointList.length;
        for ( i=0; i < nCount; i++ )
        {
            // get next layer
            szTmpLayer = this.usedLayerPointList.pop();
            
            // add it to the available list
            this.layerPointList.push( szTmpLayer );
           
            // hide layer
            CWCDHTML_SetLayerVis( szTmpLayer, false );
        }        
    }
    
    function drawNode( x, y )
    {
        // exit if no more layers are available
        if ( this.layerNodeList.length <= 0 )
            return null;
            
        // get next avaialble layer
        var szLayer = this.layerNodeList.pop();
        
        // record the layer being used
        this.usedLayerNodeList.push( szLayer );        

        // position layer
        CWCDHTML_SetLayerPos( szLayer, x - ( this.nodeWidth/2 ),
                                                    y - ( this.nodeHeight/2 ) );
        // show layer
        CWCDHTML_SetLayerVis( szLayer, true );
        
        // return
        return null;
    }
    
    function drawPoint( x, y )
    {
        // exit if no more layers are available
        if ( this.layerPointList.length <= 0 )
            return null;
            
        // get next avaialble layer
        var szLayer = this.layerPointList.pop();
        
        // record the layer being used
        this.usedLayerPointList.push( szLayer );        

        // position layer
        CWCDHTML_SetLayerPos( szLayer, x - ( this.pointWidth/2 ),
                                                    y - ( this.pointHeight/2 ) );
        // show layer
        CWCDHTML_SetLayerVis( szLayer, true );
        
        // return
        return null;
    }    
    
    function drawPoints( x1, y1, x2, y2, dSpacing, nNumPoints )
    {
        //if (nNumPoints < 1) return null;
        
        // default spacing
        var dLength = 0;
        var dSpaces = 0;
        var dActualSpacing = 0;
        var dXSpacing = 0;
        var dYSpacing = 0;
        
        with (Math)
        {
            /*if (x1 == x2) dLength = abs(y2 - y1);
            else if (y1 == y2) dLength = abs(x2 - x1);
            else*/ dLength = sqrt( pow( x2-x1, 2 ) + pow( y2-y1, 2 ) );

            // check for 0 length
            if ( dLength > 0 )
            {
                // calculate actual spacing (check for point count override)
                if ( nNumPoints == -1 )
                    dSpaces = ceil( dLength/dSpacing );
                else
                    dSpaces = nNumPoints + 1;
                dActualSpacing = dLength/dSpaces;
    
                // resolve spacing into X and Y
                dXSpacing = (x2-x1) / dLength * dActualSpacing;
                dYSpacing = (y2-y1) / dLength * dActualSpacing;
            }
            else
            {
                dSpaces = 0;
                dXSpacing = 0;
                dYSpacing = 0;
            }
        }
        
        // loop and draw a point along the line for each space less 1
        for ( var i=1; i<dSpaces; i++ )
        {
            // determine direction
            this.drawPoint( x1 + ( dXSpacing * i ), y1 + ( dYSpacing * i ) );
        }
        
        // return
        return null        
    }
    
    function slope( x1,y1,x2,y2 )
    {
        // check for vertical line (prevent div by 0 - return big number)
        if ( x1 == x2 ) return Math.pow(10,100);

        // calculate and return slope
        return (y2-y1)/(x2-x1);
    }

    function intercept( x,y,m )
    {
        // calculate the intercept
        return y-m*x;
    }
 
    function intersectsWith( oLine )
    {
    	// init vars
    	var Line1x1 = this.x1;
    	var Line1y1 = this.y1;
    	var Line1x2 = this.x2;
    	var Line1y2 = this.y2;
    	var Line2x1 = oLine.x1;
    	var Line2y1 = oLine.y1;
    	var Line2x2 = oLine.x2;
    	var Line2y2 = oLine.y2;    	
    
        // calculate slopes
        var m1 = slope( Line1x1, Line1y1, Line1x2, Line1y2 );
        var m2 = slope( Line2x1, Line2y1, Line2x2, Line2y2 );
 
        // check if lines are parallel
        if ( m1 == m2 ) return false;

        // calculate the intercepts
        var b1 = intercept( Line1x1, Line1y1, m1 );
        var b2 = intercept( Line2x1, Line2y1, m2 );

        // calculate common x coordinate
        var nCommonX = ( b2 - b1 ) / ( m1 - m2 );
        
        // calculate the y coordinate of the non-vertical line
        var nCommonY = m2*nCommonX + b2;
        if ( Line1x1 != Line1x2 )
            nCommonY = m1*nCommonX + b1;

        // check that the common x & y coordinates are on both lines
        return ( isBetween( nCommonX, Line1x1, Line1x2 ) &&
                 isBetween( nCommonX, Line2x1, Line2x2 ) &&
                 isBetween( nCommonY, Line1y1, Line1y2 ) &&
                 isBetween( nCommonY, Line2y1, Line2y2 ));

    }

    function isBetween( nValue, nPoint1, nPoint2 )
    {
        // determine if point is between based on which way the range goes
        if ( nPoint1 < nPoint2 )
    	    return ( nValue > nPoint1 && nValue < nPoint2 );
        else
	    return ( nValue > nPoint2 && nValue < nPoint1 );
    }    
}