import java.awt.image.BufferedImage;

import java.awt.Shape;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.TexturePaint;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Point2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Area;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.Line2D;
import java.awt.BasicStroke;
import java.awt.Image;
import java.awt.Frame;

import Utils.*;
import XML.*;

public class Junction implements XMLSerializable {

    public static int junctionID=1;
    final static int maxNumRoads = 4;
    final static int NORTH = 2;
    final static int EAST = 4;
    final static int SOUTH = 6;
    final static int WEST = 8;

    final static int GIVE_WAY = 0;
    final static int SIGNALLED = 1;
    final static int BRIDGE_OVER = 2;
    final static int BRIDGE_UNDER = 3;
    final static int ALTSIGNALLED = 4;

    final static int PRIORITY = 0;
    final static int NO_PRIORITY = 1;

    public int thisJunctionID;

    int laneWidth  = 13;
    TexturePaint junctionTexture;
    Shape s;
    float gridSize;
    
    float[] x = new float[9];
    float[] y = new float[9];
    int[] road = new int[9]; //4 roads attach (not 8) (so some redundant info).
    boolean[] isRoadStart = new boolean[9];
    double radAngle=0;  
    RoadDesigner parent;
    int junctionType = ALTSIGNALLED;
    int[] giveWayPriorities = new int[9];
    int totalTime = 60;
	boolean vehicle_Actuated = false, adaptive = false;
    
    BasicStroke dottedLine,normal,centerLine,thickLine;

    // A Junction is an array of 9 points
    //          1----2----3
    //          |         |
    //          8    0    4
    //          |         |
    //          7----6----5

    public Junction(float nx, float ny, int gridSize, RoadDesigner parent) {
	thisJunctionID = junctionID++;

	this.parent = parent;
	x[0] = nx;
	y[0] = ny;
	this.gridSize = gridSize;
	junctionTexture = new TexturePaint(getSignalledJunctionTexture(),
					   new Rectangle2D.Double(0,0,14,14));
	normal = new BasicStroke(1,BasicStroke.CAP_BUTT,
				 BasicStroke.JOIN_BEVEL);
	dottedLine = new BasicStroke(2, BasicStroke.CAP_ROUND,
				     BasicStroke.JOIN_ROUND,1,
				     new float[] {5,5},0);
	thickLine = new BasicStroke(4, BasicStroke.CAP_ROUND,
				    BasicStroke.JOIN_ROUND);
	centerLine = new BasicStroke(3, BasicStroke.CAP_BUTT,
				    BasicStroke.JOIN_BEVEL);
	
	for (int i=2; i<9;i+=2) road[i] = -1;
     
	setToSquare();
	setMidPoints();
	setShape();
    }


    //**********************************************************************
    //*********************** draw functions *******************************
    //**********************************************************************

    public void drawHandledJunction(Graphics2D g2d) {
	System.out.println("In drawHandledJunction");
	drawSimpleJunction(g2d);

	for (int i=2; i<9;i+=2) {    
	    g2d.setColor(Color.black);
	    g2d.fill(getHandle(x[i],y[i]));
	}
    }

    public void drawSimpleJunction(Graphics2D g2d) {
	drawSimpleJunction(g2d,x,y,s);
    }

    public void drawSimpleJunction(Graphics2D g2d,float[] x,float[] y,Shape s){

	g2d.setColor(Color.black);
	g2d.drawString("J",x[0],y[0]);
	System.out.println("draw J, x[0],y[0]= "+x[0]+", "+y[0]);

	if (junctionType == SIGNALLED || junctionType == ALTSIGNALLED) {
	    g2d.setPaint(junctionTexture);
	    g2d.fill(s);
	    g2d.setColor(Color.black);
	    g2d.draw(s);
	} else if (junctionType == GIVE_WAY) {

	    g2d.setColor(Color.lightGray);
	    g2d.fill(s);
	    g2d.setColor(Color.black);

	    float x1 = -1,y1 = -1;
	    for (int i=2; i<9;i+=2) {
		
		if (giveWayPriorities[i] == NO_PRIORITY) {
		    if (road[i] == -1) {
			g2d.setStroke(normal);
			g2d.drawLine((int)x[i-1],(int)y[i-1],
				     (int)x[(i+1)%8],(int)y[(i+1)%8]);
		    } else {
			g2d.setStroke(dottedLine);
			g2d.drawLine((int)x[i-1],(int)y[i-1],
				     (int)x[(i+1)%8],(int)y[(i+1)%8]);
		    }
		} else {
		    if (x1 == -1) {
			x1 = x[i]; y1 = y[i];
		    } else {
			g2d.setColor(Color.yellow); g2d.setStroke(centerLine);
			g2d.drawLine((int)x1,(int)y1,(int)x[i],(int)y[i]);
			g2d.setColor(Color.black);
		    }
		}
	    }
	    g2d.setStroke(normal);
	} else { 
	    g2d.setColor(Color.lightGray);
	    g2d.fill(s);
	    g2d.setColor(Color.black);
	    g2d.setStroke(thickLine);
	    for (int i=2; i<9;i+=2) {
		if (giveWayPriorities[i] != PRIORITY) {
		    g2d.drawLine((int)x[i-1],(int)y[i-1],
				 (int)x[(i+1)%8],(int)y[(i+1)%8]);
		}
	    }
	    g2d.setStroke(normal);
	}
    }

    //**********************************************************************
    //*********************** get functions ********************************
    //**********************************************************************
  
    /** Create a small square around the given point.*/
    private Shape getHandle(float x, float y) {
	return new Rectangle2D.Float(x - 2,y - 2,4,4);
    }

    /** Create a small square around the given point.*/
    private Shape getLargeHandle(float x, float y) {
	return new Rectangle2D.Float(x - 7,y - 7,14,14);
    }

    public int getID() { return thisJunctionID; }

    public Shape getShape() { return s; }

    public int getType() { return junctionType; }

    public int getPriority(int index) {
	boolean seen = false;
	for (int i=2; i<9;i+=2) {
	    if (giveWayPriorities[i] == PRIORITY) {
		if (seen && index == 1) return i;
		if (index == 0) return i;
		else seen = true;
	    }
	}
	System.out.println("Error returning from getPriority()");
	return -1;
    }

    public Line2D getNorthSide() {
	return (new Line2D.Double(x[1],y[1],x[3],y[3]));
    }
    public Line2D getEastSide() { 
	return (new Line2D.Double(x[3],y[3],x[5],y[5]));
    }
    public Line2D getSouthSide() {
	return (new Line2D.Double(x[5],y[5],x[7],y[7]));
    }
    public Line2D getWestSide() { 
	return (new Line2D.Double(x[7],y[7],x[1],y[1]));
    }

    public int getJunctionSide(Point2D p) {
	for (int i=2; i<9;i+=2) {    
	    if (getHandle(x[i],y[i]).contains(p)) return i;
	}
	System.out.println("No junctionSide Found");
	return -1;
    }

    public Point2D getMidPointsCenter() {
	return GeoUtils.getIntersectPoint(x[2],y[2],x[6],y[6],
					  x[4],y[4],x[8],y[8],false);
    }

    public int getClosestNotEmptySide(Point2D closestPoint) {
	double closestDist = 10000;
	int closestSide = -1;
	
	for (int i=2; i<9;i+=2) {    
	    if (road[i] == -1) {
		Point2D p = new Point2D.Double(x[i],y[i]);
		double dist = closestPoint.distance(p);
		if (dist < closestDist) {
		    closestSide = i;
		    closestDist = dist;
		}
	    }
	}
	return closestSide; 
    }
    
    //**********************************************************************
    //********************** test functions ********************************
    //**********************************************************************

    public boolean contains(Point2D p) {
	return s.contains(p);
    }

    public boolean intersects(Point2D p) {
	for (int i=2; i<9;i+=2) {    
	    if (getHandle(x[i],y[i]).contains(p)) return true;
	}
	return false;
    }

    public boolean testToAttach(Point2D roadStart,Point2D roadEnd, int roadID){
		
	int oldPos= -1;
	
	//First test wheather to deattach "road" from the junction
	for (int i=2; i<9;i+=2) {
	    if (road[i] == roadID) {
		road[i] = -1;
		oldPos = i; //Save position of where road was attached.
		//System.out.println("de-attached current path from " + i);
	    }
	}
	
	for (int i=2; i<9;i+=2) {
	    // Next get the junction handle.
	    Shape junctionHandle = getLargeHandle(x[i],y[i]);
	    // Now test wheather this road is to be attached.
	    // The place should be empty and if the road was attached before,
	    // it should be attached in the same place again.
	    if (road[i] == -1 && (oldPos == -1 || oldPos == i)) {
		if (junctionHandle.contains(roadStart)) {
		    road[i] = roadID;
		    isRoadStart[i] = true;
		    return true;
		} else if (junctionHandle.contains(roadEnd)) {
		    road[i] = roadID;
		    isRoadStart[i] = false;
		    return true;
		} 
	    }
	}

	//System.out.println("returned false from testToAttachJunction");
	return false;
    }

    //**********************************************************************
    //*********************** set functions ********************************
    //**********************************************************************

    public void attachRoad(int roadID, int side, boolean isStart) {
	road[side] = roadID;
	isRoadStart[side] = isStart;
    }

    public void dettachRoad(int roadID) {
	for (int i=2; i<9;i+=2) {
	    if (road[i] == roadID) road[i] = -1;
	}
    }

    public void updateJunctionLook() {
	modifyJunctionShape();
	setShape();
	rePositionAttachedRoads();

	if (junctionType == GIVE_WAY) {
	    updateGiveWayLook();
	} else if (junctionType == BRIDGE_OVER) {
	    updateBridgeOverLook() ;
	} else if (junctionType == BRIDGE_UNDER) {
	    updateBridgeUnderLook();
	}
    }

    private void updateBridgeOverLook() {
	for (int i=2; i<9;i+=2) giveWayPriorities[i] = NO_PRIORITY;
	giveWayPriorities[NORTH]= PRIORITY;
	giveWayPriorities[SOUTH]= PRIORITY;
    }

    private void updateBridgeUnderLook() {
	for (int i=2; i<9;i+=2) giveWayPriorities[i] = NO_PRIORITY;
	giveWayPriorities[EAST]= PRIORITY;
	giveWayPriorities[WEST]= PRIORITY;
    }

    private void updateGiveWayLook() {
	int max = 0;
	int side = -1;
	
	for (int i=2; i<9;i+=2) {
	    if (road[i] != -1) {
		int laneNum = parent.getRoad(road[i]).getNumLanes();
		if (max < laneNum || 
		    (max == laneNum && 
		     (road[GeoUtils.getOppositeDirection(i)]) != -1)) {
		    side = i;
		    max = laneNum;
		}
	    }
	    giveWayPriorities[i] = NO_PRIORITY;
	}
	
	giveWayPriorities[side]= PRIORITY;
	
	int opNum=-1, leftNum=-1,  rightNum=-1;
	int op = GeoUtils.getOppositeDirection(side);
	int left = GeoUtils.getLeftDirection(side);
	int right = GeoUtils.getRightDirection(side);
	
	if (road[op] != -1) opNum = parent.getRoad(road[op]).getNumLanes();
	
	if (road[left] != -1) 
	    leftNum = parent.getRoad(road[left]).getNumLanes();
	    
	if (road[right] != -1)
	    rightNum = parent.getRoad(road[right]).getNumLanes();
	
	if (opNum >= leftNum && opNum >= rightNum) {
	    if (opNum != -1) giveWayPriorities[op] = PRIORITY;
	} else {
	    if (leftNum >= rightNum) {
		if (leftNum != -1) giveWayPriorities[left] = PRIORITY;
	    } else {
		if (rightNum != -1) giveWayPriorities[right] = PRIORITY;
	    }
	}
    }

    
    public void rePositionAttachedRoads() {
	//rotateToMatchOnlyRoad();
	for (int i=2; i<9;i+=2) {
	    if (road[i] != -1) {
		parent.getRoad(road[i]).modifyRoadHandle(isRoadStart[i],
					      new Point2D.Double(x[i],y[i]));
	    }
	}
    }

    public void updateAttachedRoadShapes() {
	for (int i=2; i<9;i+=2) {
	    if (road[i] != -1) {
		parent.getRoad(road[i]).setRoadShapeVariables();
	    }
	}
    }

    private void setToSquare() {
	x[1] = x[0]-gridSize; y[1] = y[0]-gridSize;
	x[3] = x[0]+gridSize; y[3] = y[0]-gridSize;
	x[5] = x[0]+gridSize; y[5] = y[0]+gridSize;
	x[7] = x[0]-gridSize; y[7] = y[0]+gridSize;
    }

    private void setMidPoints() {
	x[2] = (x[1]+x[3])/2; y[2] = (y[1]+y[3])/2;
	x[4] = (x[3]+x[5])/2; y[4] = (y[3]+y[5])/2;
	x[6] = (x[5]+x[7])/2; y[6] = (y[5]+y[7])/2;
	x[8] = (x[7]+x[1])/2; y[8] = (y[7]+y[1])/2;
    }

    public void setShape() {
	GeneralPath jShape = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
	jShape.moveTo(x[1],y[1]);
	jShape.lineTo(x[3],y[3]);
	jShape.lineTo(x[5],y[5]);
	jShape.lineTo(x[7],y[7]);
	jShape.closePath();
	s = jShape;
    }

    //**********************************************************************
    //******************** utility functions *******************************
    //**********************************************************************

    private void modifyJunctionShape() {

    // Need to get the 8 following lengths:
    //            2     3       
    //          |----*----|                   1----2----3
    //        9 |         |4                  |         |
    //          *         *                   8    0    4
    //        8 |         |5                  |         |
    //          |----*----|                   7----6----5
    //            7     6

	float[] length = new float[10];

	for (int i=2; i<9;i+=2) {
	    if (road[i] != -1) {
		final Road tempRoad = parent.getRoad(road[i]);
		if (isRoadStart[i]) {
		    length[i] = tempRoad.getRoadLeftWidth();
		    length[i+1] = tempRoad.getRoadRightWidth();
		} else {
		    length[i] = tempRoad.getRoadRightWidth();
		    length[i+1] = tempRoad.getRoadLeftWidth();
		}
	    } else {
		length[i] = gridSize;
		length[i+1] = gridSize;
	    }
	}
	
	//get the total side length
	final float length23 = length[2]+length[3];
	final float length45 = length[4]+length[5];
	final float length67 = length[6]+length[7];
	final float length89 = length[8]+length[9];

	final float halfMaxYAxis = Math.max(length23,length67)/2;
	final float halfMaxXAxis = Math.max(length45,length89)/2;

	//balance out the slave ends to be at a midpoint
	if (length45 < length89) length[4] = halfMaxXAxis;
	if (length45 > length89) length[8] = halfMaxXAxis;
	if (length23 < length67) length[2] = halfMaxYAxis;
	if (length23 > length67) length[6] = halfMaxYAxis;

	//set points and mid-points
	x[1] = x[0] - halfMaxYAxis;          y[1] = y[0] - halfMaxXAxis;
	x[3] = x[0] + halfMaxYAxis;          y[3] = y[0] - halfMaxXAxis;
	x[5] = x[0] + halfMaxYAxis;          y[5] = y[0] + halfMaxXAxis;
	x[7] = x[0] - halfMaxYAxis;          y[7] = y[0] + halfMaxXAxis;
	
	x[2] = x[1] + length[2];             y[2] = y[0] - halfMaxXAxis;
	x[4] = x[0] + halfMaxYAxis;          y[4] = y[3] + length[4];
	x[6] = x[5] - length[6];             y[6] = y[0] + halfMaxXAxis;
	x[8] = x[0] - halfMaxYAxis;          y[8] = y[7] - length[8];

	final double oldangle = radAngle;
	rotate(radAngle);
	radAngle = oldangle;
    }
   
    public void rotate(double newAngle) {
	
	radAngle += newAngle;

	AffineTransform tx = new AffineTransform();
	tx.setToIdentity();
	tx.translate(x[0],y[0]);
	tx.rotate(newAngle);
	tx.translate(-x[0],-y[0]);

	GeneralPath pathShape = collapseToPath();	
        pathShape.transform(tx);
	s = pathShape;
	extractFromPath(pathShape);
    }

    public void rotateToMatchOnlyRoad() {
	int attachmentCount = 0, roadIndex = -1;
	Road tempRoad = null;
	
	for (int i=2; i<9;i+=2) {
	    if (road[i] != -1) {
		attachmentCount++;
		roadIndex = i;
	    }
	}

	if (attachmentCount == 1) {
	    System.out.println("Junction rotation due to singleroad.");
	    double newAngle;
	    radAngle = 0;
	    tempRoad = parent.getRoad(road[roadIndex]);
	    if (isRoadStart[roadIndex]) newAngle =tempRoad.getFirstLineAngle();
	    else newAngle = tempRoad.getLastLineAngle();
	    
	    rotate(-newAngle);
	}
    }
    
    private GeneralPath collapseToPath() {

	GeneralPath jShape = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
	
	jShape.moveTo(x[1],y[1]);
	for (int i=2;i<9;i++) jShape.lineTo(x[i],y[i]);
	jShape.closePath();
	return jShape;
    }
   
    private void extractFromPath(GeneralPath path) {

	float coOrds[] = new float[6];
	int index = 1;

	for (PathIterator i=path.getPathIterator(null);!i.isDone();i.next()){
	    int segType = i.currentSegment(coOrds);
	    if (index <9) {
		x[index]= coOrds[0]; 
		y[index++] = coOrds[1];
	    }
	} 
    }

    public void changeLocation(float newX, float newY) {

	//Move to new location
	final float xToChange = newX-x[0];
	final float yToChange = newY-y[0];

	x[1]+=xToChange; y[1]+=yToChange;
	x[3]+=xToChange; y[3]+=yToChange;
	x[5]+=xToChange; y[5]+=yToChange;
	x[7]+=xToChange; y[7]+=yToChange;

	x[0] = newX;
	y[0] = newY;

	modifyJunctionShape();

	setShape();
	rePositionAttachedRoads();
    }

    public void changeType() {
	switch (junctionType) {
	case SIGNALLED :    junctionType = GIVE_WAY;    break;
	case GIVE_WAY :     junctionType = BRIDGE_OVER; break;
	case BRIDGE_OVER :  junctionType = BRIDGE_UNDER;break;
	case BRIDGE_UNDER : junctionType = ALTSIGNALLED;break;
	case ALTSIGNALLED : junctionType = SIGNALLED;   break;
	}
    }

    public BufferedImage getSignalledJunctionTexture() {
	Frame frame = new Frame(); 
	frame.addNotify(); 
	Image offscreen = frame.createImage(14,14);
	Graphics2D offgraphics = (Graphics2D)offscreen.getGraphics();
	BasicStroke yellowPaint = new BasicStroke(2, BasicStroke.CAP_BUTT,
						  BasicStroke.JOIN_BEVEL);
	offgraphics.setColor(Color.lightGray);
	offgraphics.fillRect(0, 0, 14, 14);	
	
	offgraphics.setColor(Color.yellow);
	offgraphics.setStroke(yellowPaint); 
	
	offgraphics.drawLine(0,0,14,0);
	offgraphics.drawLine(0,7,14,7);
	offgraphics.drawLine(0,14,14,14);
	
	offgraphics.drawLine(0,0,0,14);
	offgraphics.drawLine(7,0,7,14);
	offgraphics.drawLine(14,0,14,14);
	
	return (BufferedImage)offscreen;
    }

    //**********************************************************************
    //******************** GeneralPath operations **************************
    //**********************************************************************

    public int getFirstIndex(GeneralPath p) {
	final Line2D nSide = getNorthSide();
	final Line2D eSide = getEastSide();
	final Line2D sSide = getSouthSide();
	final Line2D wSide = getWestSide();
	int index = 0;
	double x1=0,y1=0,x2=0,y2=0;
	double seg[] = new double[6];
	
	for (PathIterator i=p.getPathIterator(null);!i.isDone();i.next()) {
	    int segType = i.currentSegment(seg); 
	    
	    if (index==0) { 
		x1=seg[0];y1=seg[1]; x2=x1;y2=y1;
	    } 
	    if (index > 0) {
		x1=x2;y1=y2; x2=seg[0];y2=seg[1];

		if (GeoUtils.getIntersectPoint(x1,y1,x2,y2,nSide,false)!=null)
		    return index-1;
		if (GeoUtils.getIntersectPoint(x1,y1,x2,y2,eSide,false)!=null)
		    return index-1;
		if (GeoUtils.getIntersectPoint(x1,y1,x2,y2,sSide,false)!=null)
		    return index-1;
		if (GeoUtils.getIntersectPoint(x1,y1,x2,y2,wSide,false)!=null)
		    return index-1;
	    }
	    index++;
	}
	System.out.println("returning Error in getFirstIndex()");
	return -1;
    }

    public int getFirstDir(GeneralPath path, int index) {
	final Line2D nSide = getNorthSide();
	final Line2D eSide = getEastSide();
	final Line2D sSide = getSouthSide();
	final Line2D wSide = getWestSide();
	final Line2D line = GPathUtils.getLineSect(index,path);
	final Point2D npoint = GeoUtils.getIntersectPoint(line,nSide,false);
	final Point2D epoint = GeoUtils.getIntersectPoint(line,eSide,false);
	final Point2D spoint = GeoUtils.getIntersectPoint(line,sSide,false);
	final Point2D wpoint = GeoUtils.getIntersectPoint(line,wSide,false);
	final Point2D point = GPathUtils.getIndexPoint(index,path);
	double minDist = 10000;
	int dir = -1;

	if (npoint != null) {
	    minDist = point.distance(npoint); dir = Junction.NORTH;
	}
	if (epoint != null) {
	    final double eDist = point.distance(epoint);
	    if (eDist < minDist) {minDist = eDist; dir = Junction.EAST; }
	}
	if (spoint != null) {
	    final double sDist = point.distance(spoint);
	    if (sDist < minDist) {minDist = sDist; dir = Junction.SOUTH; }
	}
	if (wpoint != null) {
	    final double wDist = point.distance(wpoint);
	    if (wDist < minDist) {minDist = wDist; dir = Junction.WEST; }
	}
	return dir;
    }

    public Point2D getFirstIntersection(GeneralPath path, int index) {
	final Line2D nSide = getNorthSide();
	final Line2D eSide = getEastSide();
	final Line2D sSide = getSouthSide();
	final Line2D wSide = getWestSide();
	final Line2D line = GPathUtils.getLineSect(index,path);
	final Point2D npoint = GeoUtils.getIntersectPoint(line,nSide,false);
	final Point2D epoint = GeoUtils.getIntersectPoint(line,eSide,false);
	final Point2D spoint = GeoUtils.getIntersectPoint(line,sSide,false);
	final Point2D wpoint = GeoUtils.getIntersectPoint(line,wSide,false);
	final Point2D point = GPathUtils.getIndexPoint(index,path);

	double minDist = 10000;
	Point2D closestPoint=null;

	if (npoint != null) {
	    minDist = point.distance(npoint); closestPoint = npoint;
	}
	if (epoint != null) {
	    final double eDist = point.distance(epoint);
	    if (eDist < minDist) {minDist = eDist; closestPoint = epoint; }
	}
	if (spoint != null) {
	    final double sDist = point.distance(spoint);
	    if (sDist < minDist) {minDist = sDist; closestPoint = spoint; }
	}
	if (wpoint != null) {
	    final double wDist = point.distance(wpoint);
	    if (wDist < minDist) {minDist = wDist; closestPoint = wpoint; }
	}
	return closestPoint;
    }
    
    public int getFirstNonEmptyDir(GeneralPath path, int index) {
	
	Point2D closestPoint= getFirstIntersection(path,index);
	if (closestPoint == null) return -1;
	else return getClosestNotEmptySide(closestPoint);
    }

    public int getLastIndex(GeneralPath p, int pos) {
	final Line2D nSide = getNorthSide();
	final Line2D eSide = getEastSide();
	final Line2D sSide = getSouthSide();
	final Line2D wSide = getWestSide();
	int index = 0,count=0;
	double x1=0,y1=0,x2=0,y2=0;
	double seg[] = new double[6];
	
	for (PathIterator i=p.getPathIterator(null);!i.isDone();i.next()) {
	 int segType = i.currentSegment(seg); 
	 
	 if (index==0) { 
	     x1=seg[0];y1=seg[1]; x2=x1;y2=y1;
	 } 
	 if (index > 0) {
	     x1=x2;y1=y2; x2=seg[0];y2=seg[1];
	 }
	 if (index-1 == pos) {
	   final Line2D line = new Line2D.Double(x1,y1,x2,y2);
	   final Point2D nPoint = GeoUtils.getIntersectPoint(line,nSide,false);
	   final Point2D ePoint = GeoUtils.getIntersectPoint(line,eSide,false);
	   final Point2D sPoint = GeoUtils.getIntersectPoint(line,sSide,false);
	   final Point2D wPoint = GeoUtils.getIntersectPoint(line,wSide,false);
	   if (nPoint != null) count++;
	   if (ePoint != null) count++;
	   if (sPoint != null) count++;
	   if (wPoint != null) count++;
	   if (count == 2) return pos;
	 }
	 if (index-1 > pos) {
	     if (GeoUtils.getIntersectPoint(x1,y1,x2,y2,nSide,false)!=null)
		 return index-1;
	     if (GeoUtils.getIntersectPoint(x1,y1,x2,y2,eSide,false)!=null)
		 return index-1;
	     if (GeoUtils.getIntersectPoint(x1,y1,x2,y2,sSide,false)!=null)
		 return index-1;
	     if (GeoUtils.getIntersectPoint(x1,y1,x2,y2,wSide,false)!=null)
		 return index-1;
	 }
	 index++;
	}
	System.out.println("Seems to be a T-junction, pos= "+pos);
	return -1;
    }

    public int getLastDir(GeneralPath path, int index1,int index2) {

	if (index2 == -1) return -1;

	final Line2D line = GPathUtils.getLineSect(index2,path);
	final Line2D nSide = getNorthSide();
	final Line2D eSide = getEastSide();
	final Line2D sSide = getSouthSide();
	final Line2D wSide = getWestSide();

	if (index2 > index1) {
	    
	    if (GeoUtils.getIntersectPoint(line,nSide,false) != null)
		return Junction.NORTH;
	    if (GeoUtils.getIntersectPoint(line,eSide,false) != null)
		return Junction.EAST;
	    if (GeoUtils.getIntersectPoint(line,sSide,false) != null)
		return Junction.SOUTH;
	    if (GeoUtils.getIntersectPoint(line,wSide,false) != null)
		return Junction.WEST;
	    
	    System.out.println("returning Error in getLastDir()");
	    return -1;
	}
	final Point2D npoint = GeoUtils.getIntersectPoint(line,nSide,false);
	final Point2D epoint = GeoUtils.getIntersectPoint(line,eSide,false);
	final Point2D spoint = GeoUtils.getIntersectPoint(line,sSide,false);
	final Point2D wpoint = GeoUtils.getIntersectPoint(line,wSide,false);
	final Point2D point = GPathUtils.getIndexPoint(index2,path);
	double maxDist = 0;
	int dir = -1;

	if (npoint != null) {
	    maxDist = point.distance(npoint); dir = Junction.NORTH;
	}
	if (epoint != null) {
	    final double eDist = point.distance(epoint);
	    if (eDist > maxDist) {maxDist = eDist; dir = Junction.EAST; }
	}
	if (spoint != null) {
	    final double sDist = point.distance(spoint);
	    if (sDist > maxDist) {maxDist = sDist; dir = Junction.SOUTH; }
	}
	if (wpoint != null) {
	    final double wDist = point.distance(wpoint);
	    if (wDist > maxDist) {maxDist = wDist; dir = Junction.WEST; }
	}
	return dir;
    }

    public  Point2D getLastIntersection(GeneralPath path, int index1, 
				 int index2) {

	if (index2 == -1) return null;

	final Line2D line = GPathUtils.getLineSect(index2,path);
	final Line2D nSide = getNorthSide();
	final Line2D eSide = getEastSide();
	final Line2D sSide = getSouthSide();
	final Line2D wSide = getWestSide();
	final Point2D npoint = GeoUtils.getIntersectPoint(line,nSide,false);
	final Point2D epoint = GeoUtils.getIntersectPoint(line,eSide,false);
	final Point2D spoint = GeoUtils.getIntersectPoint(line,sSide,false);
	final Point2D wpoint = GeoUtils.getIntersectPoint(line,wSide,false);
	
	if (index2 > index1) {
	    if (npoint != null) return npoint;	 
	    if (epoint != null) return epoint;	   
	    if (spoint != null) return spoint;	   
	    if (wpoint != null) return wpoint;	   
	    System.out.println("Error in getLastIntersection()");
	}
	final Point2D point = GPathUtils.getIndexPoint(index2,path);
	double maxDist = 0;
        Point2D furthestPoint=null;

	if (npoint != null) {
	    maxDist = point.distance(npoint); furthestPoint=npoint;
	}
	if (epoint != null) {
	    final double eDist = point.distance(epoint);
	    if (eDist > maxDist) {maxDist = eDist; furthestPoint=epoint;}
	}
	if (spoint != null) {
	    final double sDist = point.distance(spoint);
	    if (sDist > maxDist) {maxDist = sDist; furthestPoint=spoint;}
	}
	if (wpoint != null) {
	    final double wDist = point.distance(wpoint);
	    if (wDist > maxDist) {maxDist = wDist; furthestPoint=wpoint;}
	}
	return furthestPoint;
    }

    public int getLastNonEmptyDir(GeneralPath path, int index1,int index2) {
	
	Point2D closestPoint= getLastIntersection(path,index1,index2);
	if (closestPoint == null) return -1;
	else return getClosestNotEmptySide(closestPoint);
    }


    //**********************************************************************
    //******************** XML SAVING **************************************
    //**********************************************************************
    public String getXMLName() {return "Junction";}

    public XMLElement saveSelf() { return null; }

    public void saveChilds(XMLSaver saver) { }
 
    public void loadSelf(XMLElement element) { 
	System.out.println("in Junction loadSelf()");

	junctionType = element.getAttribute("junctionType").getIntValue();
	radAngle = element.getAttribute("radAngle").getDoubleValue();
	vehicle_Actuated = element.getAttribute("Actuated").getBooleanValue();
	adaptive = element.getAttribute("Adaptive").getBooleanValue();
	
	for (int i=0; i < 9; i++) {
	  x[i] = element.getAttribute("x"+i).getFloatValue();
	  y[i] = element.getAttribute("y"+i).getFloatValue();
	}

	System.out.println("x[0],y[0]= "+x[0]+", "+y[0]);
    }

    public void loadChilds(XMLLoader loader) { 
	//System.out.println("In Junction loadChilds()");
    }

}
