/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: OverlapWorker.java
 * Written by Team 7: Felix Schmidt
 * 
 * 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.forceDirected2.forceDirected.staged;

import com.sun.electric.tool.placement.PlacementFrame.PlacementNode;
import com.sun.electric.tool.placement.forceDirected2.PlacementForceDirectedStaged;
import com.sun.electric.tool.placement.forceDirected2.forceDirected.OverlapDirection;
import com.sun.electric.tool.placement.forceDirected2.forceDirected.util.CheckboardingField;
import com.sun.electric.tool.placement.forceDirected2.forceDirected.util.history.IOverlapHistory;
import com.sun.electric.tool.placement.forceDirected2.forceDirected.util.history.OverlapHistorySync;
import com.sun.electric.tool.placement.forceDirected2.utils.concurrent.EmptyException;
import com.sun.electric.tool.placement.forceDirected2.utils.concurrent.StageWorker;

public class OverlapWorker extends StageWorker {

	private double threshold;

//	private PlacementNode node = null;
//
//	private Random rand;

	@SuppressWarnings("unused")
	private int index;

	public OverlapWorker(double threshold, int index) {
		this.threshold = threshold;
//		this.rand = new Random(System.currentTimeMillis());
		this.index = index;
	}

	private void exchangeAndPlace(CheckboardingField field1, CheckboardingField field2, CheckboardingField[][] fields) {
		IOverlapHistory<PlacementNode> history = OverlapHistorySync.getInstance();

		PlacementNode node1 = field1.getNode();
		PlacementNode node2 = field2.getNode();

		if ((field1 != field2) && history.isMovementInHistory(node1, node2)) {

			this.exchangeAndPlace(field1, field1, fields);
			this.exchangeAndPlace(field2, field2, fields);

		} else {
			field2.placeCentralized(node1);
			field1.placeCentralized(node2);
			history.saveHistory(node1, node2);
			PlacementForceDirectedStaged.incMovementCounter();

		}

	}

	private void handleOverlappingInX(CheckboardingField[][] fieldPart, int i, int j) {
		double ovX = fieldPart[i][j].getOverlappingFractionInX();
		double ovY = fieldPart[i][j].getOverlappingFractionInY();

		OverlapDirection dir = fieldPart[i][j].getOverlapDirection();

		if (ovX > this.threshold) {

			int newX;
			CheckboardingField newField = null;
			if (OverlapDirection.isEast(dir)) {

				newX = j + 1;
				if (newX < fieldPart[i].length) {
					newField = fieldPart[i][newX];
				}

			} else if (OverlapDirection.isWest(dir)) {

				newX = j - 1;
				if (newX >= 0) {
					newField = fieldPart[i][newX];
				}

			}

			if (newField == null) {
				newField = fieldPart[i][j];
			}

			this.exchangeAndPlace(fieldPart[i][j], newField, fieldPart);

		} else if (ovY > this.threshold) {
			this.handleOverlappingInY(fieldPart, i, j);
		}
	}

	private void handleOverlappingInY(CheckboardingField[][] fieldPart, int i, int j) {
		double ovX = fieldPart[i][j].getOverlappingFractionInX();
		double ovY = fieldPart[i][j].getOverlappingFractionInY();

		OverlapDirection dir = fieldPart[i][j].getOverlapDirection();

		if (ovY > this.threshold) {

			int newY;
			CheckboardingField newField = null;
			if (OverlapDirection.isNorth(dir)) {

				newY = i + 1;
				if (newY < fieldPart.length) {
					newField = fieldPart[newY][j];
				}

			} else if (OverlapDirection.isSouth(dir)) {

				newY = j - 1;
				if (newY >= 0) {
					newField = fieldPart[newY][j];
				}

			}

			if (newField == null) {
				newField = fieldPart[i][j];
			}

			this.exchangeAndPlace(fieldPart[i][j], newField, fieldPart);
		} else if (ovX > this.threshold) {
			this.handleOverlappingInX(fieldPart, i, j);
		}

	}

	public void run() {
		while (!this.abort.booleanValue()) {

			try {
				PlacementDTO data = this.stage.getInput(this).get();
				CheckboardingField[][] fieldPart = data.getFieldPart();

				for (int i = 0; i < fieldPart.length; i++) {
					for (int j = 0; j < fieldPart[i].length; j++) {
						if ((fieldPart[i][j] != null) && (fieldPart[i][j].isOverlappingBiggerThreshold(this.threshold))) {
							int proc = fieldPart[i][j].getCoordX() + fieldPart[i][j].getCoordY();
							if ((proc % 2) == 0) {
								this.handleOverlappingInX(fieldPart, i, j);
							} else {
								this.handleOverlappingInY(fieldPart, i, j);
							}
						}
					}
				}

				this.stage.sendToNextStage(data);

			} catch (EmptyException e) {
				Thread.yield();
			}
		}
	}

}
