/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Metric.java
 * Written by Team 6: Sebastian Roether, Jochen Lutz
 * 
 * This code has been developed at the Karlsruhe Institute of Technology (KIT), Germany, 
 * as part of the course "Multicore Programming in Practice: Tools, Models, and Languages".
 * Contact instructor: Dr. Victor Pankratius (pankratius@ipd.uka.de)
 *
 * Copyright (c) 2010, Static Free Software. 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.placement.simulatedAnnealing2;

import com.sun.electric.tool.placement.PlacementFrame.PlacementNetwork;
import com.sun.electric.tool.placement.PlacementFrame.PlacementNode;

import java.util.List;
import java.util.Map;

public abstract class Metric
{
	/**
	 * Method that calculates how much a node overlaps with another node 
	 * @param node1
	 * @param node2
	 * @return The size of the overlapping area
	 */
	public double overlap(ProxyNode node1, ProxyNode node2)
	{	
		// Check if both nodes are close enough in the x-dimension to overlap
		double X1 = node1.getPlacementX();
		double X2 = node2.getPlacementX();
		double width1  = node1.width / 2;
		double width2  = node2.width / 2;
		double distX = Math.abs(X1 - X2);
		double minDistX = width1 + width2;

		if(distX < minDistX)
		{
			// Check if both nodes are close enough in the y-dimension to overlap
			double Y1 = node1.getPlacementY();
			double Y2 = node2.getPlacementY();
			double height1 = node1.height / 2;
			double height2 = node2.height / 2;
			double distY = Math.abs(Y1 - Y2);
			double minDistY = height1 + height2;

			if(distY < minDistY)
			{
				double minX1 = X1 - width1;
				double minX2 = X2 - width2;
				double maxX1 = X1 + width1;
				double maxX2 = X2 + width2;
				double minY1 = Y1 - height1;
				double minY2 = Y2 - height2;
				double maxY1 = Y1 + height1;
				double maxY2 = Y2 + height2;

				return (Math.min(maxX2, maxX1) - Math.max(minX1, minX2)) * (Math.min(maxY2, maxY1) - Math.max(minY1, minY2));
			}
		}
		
		return 0;
	}

	/**
	 * Method that calculates how much a node overlaps with a set of nodes
	 * @param node
	 * @param nodes
	 * @return The sum of the individual overlaps of that node with every node in the set
	 */
	public double overlap( ProxyNode node, List<ProxyNode> nodes ) {
		double overlap = 0;

		for ( ProxyNode n : nodes )
			if(n != node)
				overlap += overlap( node, n );

		return overlap;
	}
	
	/**
	 * Method that calculates how much nodes from a set of nodes overlap with each other
	 * @param nodes
	 * @return The sum of of the individual overlaps of that node with every node in the set
	 */
	public double overlap(List<ProxyNode> nodes)
	{
		double overlap = 0;
		for(int i = 0; i < nodes.size(); i++)
			for(int j = i + 1; j < nodes.size(); j++)
				overlap += overlap( nodes.get(i), nodes.get(j) );
		
		return overlap;
	}
	
	/**
	 * Method that calculates the size of the area cover by a set of nodes.
	 * (i.e. the size of the area of the bounding box of all cells)
	 * @param nodes
	 * @return The size of the used rectangular area
	 */
	public double area( List<ProxyNode> nodes ) {
		double minX = Double.MAX_VALUE, minY = Double.MAX_VALUE, maxX = -Double.MAX_VALUE, maxY = -Double.MAX_VALUE;

		for ( ProxyNode node : nodes ) {
			double currMinX = node.getPlacementX() - node.width/2;
			double currMinY = node.getPlacementY() - node.height/2;
			double currMaxX = node.getPlacementX() + node.width/2;
			double currMaxY = node.getPlacementY() + node.height/2;

			if ( currMinX < minX ) minX = currMinX;
			if ( currMinY < minY ) minY = currMinY;
			if ( currMaxX > maxX ) maxX = currMaxX;
			if ( currMaxY > maxY ) maxY = currMaxY;
		}

		return ( maxX - minX ) * ( maxY - minY );
	}

	/**
	 * Method that approximates the conductor length of a net
	 * @param network
	 */
	public abstract double netLength(PlacementNetwork network);
	
	/**
	 * Method that approximates the conductor length of a net when proxies are used
	 * @param network
	 * @param proxyMap
	 * @param originals
	 * @param replacements
	 */
	public abstract double netLength(PlacementNetwork network, Map<PlacementNode, ProxyNode> proxyMap, ProxyNode[] originals, ProxyNode[] replacements);
	
	/**
	 * Convenience method
	 * @param network
	 * @param proxyMap
	 */
	public double netLength(PlacementNetwork network,  Map<PlacementNode, ProxyNode> proxyMap)
	{
		return netLength(network, proxyMap, new ProxyNode[]{}, new ProxyNode[]{});
	}

	/**
	 * Method that approximates the conductor length of a set of nets when proxies are used
	 * @param networks
	 * @param proxyMap
	 */
	public double netLength(List<PlacementNetwork> networks, Map<PlacementNode, ProxyNode> proxyMap)
	{
		double length = 0;
		
		for(PlacementNetwork net : networks)
			length += netLength(net, proxyMap, new ProxyNode[]{}, new ProxyNode[]{}) ;
		
		return length;
	}

	/**
	 * Method that approximates the conductor length of a set of nets
	 * @param networks
	 */
	public double netLength(List<PlacementNetwork> networks)
	{
		double length = 0;
		
		for(PlacementNetwork net : networks)
			length += netLength(net) ;
		
		return length;
	}	
}
