/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: DetailedRouter.java
 * Written by: Alexander Herzog, Martin Fietz (Team 4)
 *
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.tool.routing.experimentalLeeMoore2;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import com.sun.electric.tool.routing.RoutingFrame.RoutingLayer;
import com.sun.electric.tool.routing.experimentalLeeMoore2.GlobalRouterV3.RegionToRoute;
import com.sun.electric.tool.routing.experimentalLeeMoore2.RoutingFrameLeeMoore.Coordinate;

public final class DetailedRouter {

	private final int numThreads;
	private long timeout;

	private RegionToRoute[] regions;

	private DetailedRoutingSolution solutions;
	private List<Integer> unrouted;

	private List<DetailedRouterWorker> workers;

	public DetailedRouter(int numThreads, RoutingLayer[] metalLayers,
			RegionToRoute[] regions, double tileSize, boolean debug ) {
		this.numThreads = numThreads;
		unrouted = new ArrayList<Integer>();
		workers = new ArrayList<DetailedRouterWorker>(regions.length);
		solutions = new DetailedRoutingSolution();
		for (int i = 0; i < regions.length; i++) {
			DetailedRouterWorker d = new DetailedRouterWorker(regions[i], metalLayers, tileSize);
			if( debug ) {
				d.enableOutput();
			}
			workers.add(d);
		}
	}

	public void start() {
		unrouted.clear();
		for (int i = 0; i < regions.length; i++) {
			workers.get(i).setRegion(regions[i]);
		}
//		debug("\n");
		ExecutorService es = Executors.newFixedThreadPool(numThreads);
		for (DetailedRouterWorker w : workers) {
			es.submit(w);
			//w.run();
		}
		es.shutdown();
		try {
			es.awaitTermination(timeout, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//get all routed patches
		for( DetailedRouterWorker w : workers ) {
			if( w.isDone() ) {
//				for(SegPart sp : w.getSolution().keySet()){
//					assert(!solutions.containsKey(sp));
//				}
				solutions.putAll( w.getSolution() );
			}
		}
//		debug("\n");
		//recognize each routes not completed
		for (RegionToRoute region : regions) {
			for (SegPart segment : region.segments_to_route) {
//				assert(segment.segment_part.size() == 2);
				if (!solutions.containsKey(segment)
						&& !unrouted.contains(segment.id)) {
					unrouted.add(segment.id);
				}
			}
		}

		// propagate findings to all workers and clean them
		for (DetailedRouterWorker w : workers) {
			w.removeSolutions( unrouted );
		}
		// clean solutions
		for( Iterator<Map.Entry<SegPart, List<Coordinate>>> it = solutions.entrySet().iterator(); it.hasNext(); ) {
			Map.Entry<SegPart,List<Coordinate>> s = it.next();
			if( unrouted.contains( s.getKey().id ) ) {
				it.remove();
			}
		}
		
		// clean region
		for (RegionToRoute region : regions) {
			for (Iterator<SegPart> itr = region.segments_to_route.iterator(); itr.hasNext(); ) {
				SegPart segment = itr.next();
				if (unrouted.contains(segment.id)) {
					itr.remove();
				}
			}
		}
	}

	public void setTimeout(long seconds) {
		this.timeout = seconds;
	}

	public void setRegions(RegionToRoute[] regions) {
		this.regions = regions;
	}

	public List<Integer> getUnroutables() {
		return unrouted;
	}

	public void writeSolution() {
		for (Map.Entry<SegPart, List<Coordinate>> s : solutions.entrySet()) {
			SegPart toRoute = s.getKey();
			List<Coordinate> routed = s.getValue();
			if (routed.size() > 2 && toRoute.segment_part.size() <= 2) {	//pay attention not to refill an earlier routed patch
//				assert(toRoute.segment_part.size() == 2);
				toRoute.segment_part.addAll( 1, routed );
			}
		}
		solutions.clear();
	}
	
//	public void debugPrintCoordinateList(List<Coordinate> cs) {
//		for (Coordinate c : cs)
//			debugPrintCoordinate(c);
//		debug(".\n");
//	}
//
//	public void debugPrintCoordinate(Coordinate c) {
//		debug("(" + c.x + "," + c.y + "," + c.layer + ") ");
//	}
//	
//	private void debug( String s ) {
//		if( this.enableOutput ) {
//			System.out.print( s );
//		}
//	}

	public static final class DetailedRoutingSolution extends
			HashMap<SegPart, List<Coordinate>> {
		/**
		 * 
		 */
		private static final long serialVersionUID = -3806453821768987428L;

		@Override
		public DetailedRoutingSolution clone() {
			return (DetailedRoutingSolution) super.clone();
		}
	}
}

//class FailReport{
//	Rectangle2D bounds;
//	int seg_id;
//	
//	public FailReport(int seg_id, Rectangle2D bounds){
//		this.bounds = bounds;
//		this.seg_id = seg_id;
//	}
//}