package busrider;

/**
 This class is used by the conductor, when a Strategy fails to select
 a valid Segment for a player to traverse.
**/

class Router {

    /* We relate the cost of boarding a bus to the opportunity cost
       of travelling some distance. We assume the payoffs are roughly
       equal to the distance, and the average roll is eight. Therefore
       the cost of travelling is $1/8 per unit distance. Costs will be
       scaled by eight. */
    /* packaged */ final static int
	BOARDING_SCALE = 8,
	DISTANCE_COST  = 1;


    /* ----- internal classes ----- */

    /* For each segment-end to which we find a path, we keep the cost of
       getting there, and how we got there. */
    private static class SegInfo {
	public Segment seg;   // the segment
	public Junction junc; // which end of the segment
	public boolean done;  /* whether the current path is cheapest
				 and we have explored the fan-out */
	public int cost;      // cost of getting here
	public SegInfo prev;  // previous segment-end

	public SegInfo(Segment sg, Junction jend,
		       int playerIndex, Junction here, Route current)
	{
	    seg = sg;
	    junc = jend;
	    done = false;
	    cost = Integer.MAX_VALUE;
	    prev = null;

	    if (junc == here) {
		// calculate boarding cost
		Route segroute = seg.getRoute();
		int owner = Game.getInstance().getOwner(segroute);
		if ((segroute == current) || (owner == playerIndex)) {
		    cost = 0;
		} else if (owner < 0) {
		    cost = BOARDING_SCALE * Game.SMALL_FARE;
		} else {
		    cost = BOARDING_SCALE * Game.LARGE_FARE;
		}
	    }
	}

    }


    /* ----- methods ----- */

    /**
     picks the next segment to traverse.
    **/
    public static Segment pickSegment(int playerIndex,
	Junction here, Junction there, Route current)
    {
	if ((here == null) || (there == null)) return null;
	if (here.equals(there)) return null;

	Game ga = Game.getInstance();
	Board bo = ga.getBoard();

	/* -- initialize segInfos -- */
	int segquan = bo.getSegmentQuan();
	int sgiquan = segquan * 2; // one for each segment-end
	SegInfo[] infos = new SegInfo[sgiquan];
	int ix = 0;
	for (int sx = 0; sx < segquan; sx += 1) {
	    Segment seg = bo.getSegment(sx);
	    infos[ix] = new SegInfo(seg, seg.getAjunc(),
				    playerIndex, here, current);
	    ix += 1;
	    infos[ix] = new SegInfo(seg, seg.getBjunc(),
				    playerIndex, here, current);
	    ix += 1;
	}

	/* -- find the best route, Here to There -- */
	SegInfo bestsgi;
	for ( ; ; ) {
	    /* find the cheapest non-done segInfo.
	       There can be no cheaper way to get there, since we have
	       checked all segments from done-segInfos, and all other
	       paths go through equally- or more- expensive segInfos. */
	    bestsgi = null;
	    int mincost = Integer.MAX_VALUE;
	    for (int sx = 0; sx < sgiquan; sx += 1) {
		SegInfo sgi = infos[sx];
		if ((! sgi.done) && (sgi.cost < mincost)) {
		    mincost = sgi.cost;
		    bestsgi = sgi;
		}
	    }
	    if (bestsgi == null) {
		throw new FailureException("Router pgmerror #1");
	    }
	    bestsgi.done = true;

	    /* If Best's junction is There, we have finished the path. */
/* exit */  if (bestsgi.junc == there) break;

	    /* Try all segments from Bestsgi's junction. */
	    int jsq = bestsgi.junc.getSegmentQuan();
	    for (int jsx = 0; jsx < jsq; jsx += 1) {
		Segment jseg = bestsgi.junc.getSegment(jsx);
		int toidx = jseg.getIndex() * 2;
		    // index of next segInfo, if we end up at A-junc

		int cost = bestsgi.cost;
		if (bestsgi.seg == jseg) {
		    // same segment: traverse
		    cost += jseg.getLength() * DISTANCE_COST;

		    if (jseg.getAjunc() == bestsgi.junc) toidx += 1;
			// use segInfo at other end
		} else {
		    // different segment: check boarding cost
		    if ((bestsgi.prev == null) ||
			(bestsgi.prev.seg.getRoute() != jseg.getRoute()))
		    {
			// need to board
			int owner = ga.getOwner(jseg.getRoute());
			if (owner < 0) {
			    cost += BOARDING_SCALE * Game.SMALL_FARE;
			} else if (owner != playerIndex) {
			    cost += BOARDING_SCALE * Game.LARGE_FARE;
			}
		    }

		    if (jseg.getBjunc() == bestsgi.junc) toidx += 1;
			// use segInfo at this end
		}
		SegInfo tosgi = infos[toidx];

		if (cost < tosgi.cost) {
		    // we have a cheaper way to get there
		    tosgi.cost = cost;
		    tosgi.prev = bestsgi;
		}
	    }
	}

	/* We have the cheapest way from Here to There.
	   Backtrack from There(bestsgi) to Here. */
	while (bestsgi.prev.junc != here) {
	    sgiquan -= 1;
	    if (sgiquan < 0) {
		throw new FailureException("Router pgmerror #2");
	    }
	    bestsgi = bestsgi.prev;
	}
	return bestsgi.seg;
    }

}
