/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: CellChangeJobs.java
 *
 * Copyright (c) 2006 Sun Microsystems and Static Free Software
 *
 * 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 Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 */
package com.sun.electric.tool.user;
import com.sun.electric.database.IdMapper;
import com.sun.electric.database.ImmutableArcInst;
import com.sun.electric.database.geometry.EGraphics;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.GenMath;
import com.sun.electric.database.geometry.Orientation;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.hierarchy.View;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.text.Name;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.UserInterface;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.user.ui.EditWindow;
import com.sun.electric.tool.user.ui.WindowContent;
import com.sun.electric.tool.user.ui.WindowFrame;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * Class for Jobs that make changes to the cells.
 */
public class CellChangeJobs
{
	// constructor, never used
	private CellChangeJobs() {}
	/****************************** DELETE A CELL ******************************/
	/**
	 * Class to delete a cell in a new thread.
	 */
	public static class DeleteCell extends Job
	{
		Cell cell;
		public DeleteCell(Cell cell)
		{
			super("Delete " + cell, User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
			this.cell = cell;
			startJob();
		}
		public boolean doIt() throws JobException
		{
			// check cell usage once more
			if (cell.isInUse("delete", false, true)) return false;
			cell.kill();
			return true;
		}
	}
	/**
	 * This class implement the command to delete a list of cells.
	 */
	public static class DeleteManyCells extends Job
	{
		private List<Cell> cellsToDelete;
		public DeleteManyCells(List<Cell> cellsToDelete)
		{
			super("Delete Multiple Cells", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
			this.cellsToDelete = cellsToDelete;
			startJob();
		}
		public boolean doIt() throws JobException
		{
			// iteratively delete, allowing cells in use to be deferred
			boolean didDelete = true;
			while (didDelete)
			{
				didDelete = false;
				for (int i=0; i<cellsToDelete.size(); i++)
				{
					Cell cell = cellsToDelete.get(i);
					// if the cell is in use, defer
					if (cell.isInUse(null, true, true)) continue;
					// cell not in use: remove it from the list and delete it
					cellsToDelete.remove(i);
					i--;
					System.out.println("Deleting " + cell);
					cell.kill();
					didDelete = true;
				}
			}
			// warn about remaining cells that were in use
			for(Cell cell : cellsToDelete)
				cell.isInUse("delete", false, true);
			return true;
		}
		public void terminateOK()
		{
			System.out.println("Deleted " + cellsToDelete.size() + " cells");
			EditWindow.repaintAll();
		}
	}
	/****************************** RENAME CELLS ******************************/
	/**
	 * Class to rename a cell in a new thread.
	 */
	public static class RenameCell extends Job
	{
		private Cell cell;
		private String newName;
		private String newGroupCell;
		private IdMapper idMapper;
		public RenameCell(Cell cell, String newName, String newGroupCell)
		{
			super("Rename " + cell, User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
			this.cell = cell;
			this.newName = newName;
			this.newGroupCell = newGroupCell;
			startJob();
		}
		public boolean doIt() throws JobException
		{
			idMapper = cell.rename(newName, newGroupCell);
			fieldVariableChanged("idMapper");
			return true;
		}
		public void terminateOK()
		{
			User.fixStaleCellReferences(idMapper);
		}
	}
	/**
	 * Class to rename a cell in a new thread.
	 */
	public static class DeleteCellGroup extends Job
	{
		List<Cell> cells;
		public DeleteCellGroup(Cell.CellGroup group)
		{
			super("Delete Cell Group", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
			cells = new ArrayList<Cell>();
			for(Iterator<Cell> it = group.getCells(); it.hasNext(); )
			{
				cells.add(it.next());
			}
			startJob();
		}
		public boolean doIt() throws JobException
		{
			for(Cell cell : cells)
			{
				// Doesn't check cells in the same group
				// check cell usage once more
				if (cell.isInUse("delete", false, false))
					return false;
			}
			// Now real delete
			for(Cell cell : cells)
			{
				cell.kill();
			}
			return true;
		}
	}
	/**
	 * Class to rename a cell in a new thread.
	 */
	public static class RenameCellGroup extends Job
	{
		Cell cellInGroup;
		String newName;
		public RenameCellGroup(Cell cellInGroup, String newName)
		{
			super("Rename Cell Group", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
			this.cellInGroup = cellInGroup;
			this.newName = newName;
			startJob();
		}
		public boolean doIt() throws JobException
		{
			// see if all cells in the group have the same name
			boolean allSameName = true;
			String lastName = null;
			for(Iterator<Cell> it = cellInGroup.getCellGroup().getCells(); it.hasNext(); )
			{
				String cellName = it.next().getName();
				if (lastName != null && !lastName.equals(cellName))
				{
					allSameName = false;
					break;
				}
				lastName = cellName;
			}
			List<Cell> cells = new ArrayList<Cell>();
			for(Iterator<Cell> it = cellInGroup.getCellGroup().getCells(); it.hasNext(); )
				cells.add(it.next());
			String newGroupCell = null;
			for(Cell cell : cells)
			{
				if (allSameName)
				{
					cell.rename(newName, newName);
				} else
				{
					if (newGroupCell == null)
					{
						System.out.println("Renaming is not possible because cells in group don't have same root name.");
						System.out.println("'" + newName + "' was added as prefix.");
						newGroupCell = newName + cell.getName();
					}
					cell.rename(newName+cell.getName(), newGroupCell);
				}
			}
			return true;
		}
	}
	/****************************** SHOW CELLS GRAPHICALLY ******************************/
	/**
	 * This class implement the command to make a graph of the cells.
	 */
	public static class GraphCells extends Job
	{
		private static final double TEXTHEIGHT = 2;
		private Cell top;
		private Cell graphCell;
		private static class GraphNode
		{
			String    name;
			int       depth;
			int       clock;
			double    x, y;
			double    yoff;
			NodeInst  pin;
			NodeInst  topPin;
			NodeInst  botPin;
			GraphNode main;
		}
		public GraphCells(Cell top)
		{
			super("Graph Cells", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
			this.top = top;
			startJob();
		}
		public boolean doIt() throws JobException
		{
			// create the graph cell
			graphCell = Cell.newInstance(Library.getCurrent(), "CellStructure");
			fieldVariableChanged("graphCell");
			if (graphCell == null) return false;
			if (graphCell.getNumVersions() > 1)
				System.out.println("Creating new version of cell: " + graphCell.getName()); else
					System.out.println("Creating cell: " + graphCell.getName());
			// create GraphNodes for every cell and initialize the depth to -1
			Map<Cell,GraphNode> graphNodes = new HashMap<Cell,GraphNode>();
			for(Iterator<Library> it = Library.getLibraries(); it.hasNext(); )
			{
				Library lib = it.next();
				if (lib.isHidden()) continue;
				for(Iterator<Cell> cIt = lib.getCells(); cIt.hasNext(); )
				{
					Cell cell = cIt.next();
					GraphNode cgn = new GraphNode();
					cgn.name = cell.describe(false);
					cgn.depth = -1;
					graphNodes.put(cell, cgn);
				}
			}
			// find all top-level cells
			int maxDepth = 0;
			if (top != null)
			{
				GraphNode cgn = graphNodes.get(top);
				cgn.depth = 0;
			} else
			{
				for(Iterator<Cell> cIt = Library.getCurrent().getCells(); cIt.hasNext(); )
				{
					Cell cell = cIt.next();
					if (cell.getNumUsagesIn() == 0)
					{
						GraphNode cgn = graphNodes.get(cell);
						cgn.depth = 0;
					}
				}
			}
			double xScale = 2.0 / 3.0;
			double yScale = 20;
			double yOffset = TEXTHEIGHT * 1.25;
			double maxWidth = 0;
			// now place all cells at their proper depth
			boolean more = true;
			while (more)
			{
				more = false;
				for(Iterator<Library> it = Library.getLibraries(); it.hasNext(); )
				{
					Library lib = it.next();
					if (lib.isHidden()) continue;
					for(Iterator<Cell> cIt = lib.getCells(); cIt.hasNext(); )
					{
						Cell cell = cIt.next();


Answer: 						GraphNode cgn = graphNodes.get(cell);