/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: SteinerTree.java
 * Written by: Steven Rubin.
 *
 * Copyright (c) 2012, 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.database.topology;

import com.sun.electric.database.geometry.EPoint;

import java.util.ArrayList;
import java.util.List;

/**
 * Class for constructing a Steiner Tree.
 */
public class SteinerTree
{
	private List<SteinerTreePortPair> pairs;

	/**
	 * Interface to define a point in the Steiner Tree that needs to be organized properly.
	 * The PortInst class implements this.
	 */
	public interface SteinerTreePort
	{
		public EPoint getCenter();
	}

	/**
	 * Class that defines a branch of the Steiner Tree, with two SteinerTreePort objects.
	 */
	public static class SteinerTreePortPair
	{
		private SteinerTreePort p1, p2;

		public SteinerTreePortPair(SteinerTreePort p1, SteinerTreePort p2)
		{
			this.p1 = p1;
			this.p2 = p2;
		}

		public SteinerTreePort getPort1() { return p1; }

		public SteinerTreePort getPort2() { return p2; }
	}

	/**
	 * Constructor takes a list of 
	 * @param portList a List of SteinerTreePort objects that are to be organized into a Steiner Tree.
	 */
	public SteinerTree(List<SteinerTreePort> portList)
	{
		// the final list of edges in the Steiner tree
		pairs = new ArrayList<SteinerTreePortPair>();
		if (portList.size() < 2) return;

		// a list of points that are already in the tree (initially contains the first point, a random choice)
		List<SteinerTreePort> seen = new ArrayList<SteinerTreePort>();
		seen.add(portList.get(0));

		// initial list of points to be added to the tree (excluding the first point)
		List<SteinerTreePort> remaining = new ArrayList<SteinerTreePort>();
		for(int i=1; i<portList.size(); i++) remaining.add(portList.get(i));

		// iteratively find the closest unassigned point to the tree
		while (remaining.size() > 0)
		{
			// find the closest to anything in the Steiner tree
			double bestDist = Double.MAX_VALUE;
			SteinerTreePort bestRem = null, bestSeen = null;
			for(SteinerTreePort piRem : remaining)
			{
				for(SteinerTreePort piSeen : seen)
				{
					double dist = piRem.getCenter().distance(piSeen.getCenter());
					if (dist < bestDist)
					{
						bestDist = dist;
						bestRem = piRem;
						bestSeen = piSeen;
					}
				}					
			}
			if (bestRem != null)
			{
				SteinerTreePortPair pp = makeTreeBranch(bestRem, bestSeen);
				pairs.add(pp);
				remaining.remove(bestRem);
				seen.add(bestRem);
			}
		}
	}

	/**
	 * Overridable method to create a SteinerTreePortPair from two SteinerTreePort objects.
	 * @param p1 the first SteinerTreePort.
	 * @param p2 the second SteinerTreePort.
	 * @return a SteinerTreePortPair that encapsulates this branch of the Steiner Tree.
	 */
	public SteinerTreePortPair makeTreeBranch(SteinerTreePort p1, SteinerTreePort p2)
	{
		return new SteinerTreePortPair(p1, p2);
	}

	/**
	 * Method to return the Steiner Tree.
	 * @return a List of SteinerTreePortPair objects that describes the Steiner Tree.
	 */
	public List<SteinerTreePortPair> getTreeBranches()
	{
		return pairs;
	}
}
