// Copyright 1996, David Neto
import java.awt.*;
import java.lang.*;	// needed for sleep

public class MergeSortActivation extends Panel {
	static int MSAInstanceNumber = 0;

	MergeSortApp msa;
	Ticker ticker;
	Sorter sorter;
	ComparisonLabel CCLabel;
	
	int rewindMaxVal, rewindIntValues[], rewindLo, rewindHi;

	int myInstanceNumber;
	GridBagLayout gridBag;
	GridBagConstraints c;
	Insets theseInsets;
	boolean isActive;

	boolean showBound, showCount;
	MergeSortActivation left, right, parent;
	int offset; // number of parent elements preceding the first element in this activation
	BarPanel bp;
	int len;
	int maxVal;
	int comparisonCount, comparisonUpperBound;
	int thisLo, thisHi;

	public MergeSortActivation(
	MergeSortApp msa, 
	Ticker ticker, 
	MergeSortActivation parent, 
	int parentLo, 
	int maxVal, 
	int values[], 
	int lo, 
	int hi, 
	boolean showBound, 
	boolean showCount) {
		myInstanceNumber = MSAInstanceNumber++;
		this.msa = msa;
		this.ticker = ticker;
		theseInsets = new Insets(2,6,0,0);
		setLayout(new GridLayout(1,0));
		this.parent = parent;
		this.offset = lo-parentLo;
		this.showBound = showBound;
		this.showCount = showCount;
		this.maxVal = maxVal;
		this.len = hi-lo;
		thisLo = lo;
		thisHi = hi;
		if ( hi-lo > 1 ) {
			int mid = (lo + hi) / 2;
			left = new MergeSortActivation(msa,ticker,this,lo,maxVal,values,lo,mid,showBound,showCount);
			right = new MergeSortActivation(msa,ticker,this,lo,maxVal,values,mid,hi,showBound,showCount);
			comparisonUpperBound = 
				left.getComparisonUpperBound() + 
				right.getComparisonUpperBound() + 
				hi-lo;	/* This can be hi-lo-1, but I don't think there is 
						a symbol for <= */
		} else {
			left = right = null;
			comparisonUpperBound = 0;
		}
		bp = new BarPanel(maxVal,values,lo,hi);

		// Add the panels to the Container
		gridBag = new GridBagLayout();
		setLayout(gridBag);
		c = new GridBagConstraints();
		c.weighty = -1.0;
		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.HORIZONTAL;
		c.anchor = GridBagConstraints.NORTH;
//		Button UBButton = new Button("Upper bound");
//		gridBag.setConstraints(UBButton,c);
//		add(UBButton);
		CCLabel = new ComparisonLabel(comparisonUpperBound);
		if ( comparisonUpperBound > 0 ) {	// Only add CCLabel if > 0.
			gridBag.setConstraints(CCLabel,c);
			add(CCLabel);
		}
		c.fill = GridBagConstraints.NONE;
		gridBag.setConstraints(bp,c);
		add(bp);

		if ( hi-lo > 1 ) {
			c.gridwidth = 1;
			gridBag.setConstraints(left,c);
			gridBag.setConstraints(right,c);
			add(left);
			add(right);
		}
		if ( parent == null ) {
			sorter = new Sorter(msa,this);
		}
		setActive(false);
	}


	public synchronized void start() {
		sorter.start();
		if ( ! sorter.isAlive() ) {
			sorter = new Sorter(msa,this);
			sorter.start();
		}
	}

	public synchronized void stop() {
		sorter.stop();
	}

// Don't call setInput unless the length stays the same.
	public synchronized void setInput( int maxVal, int intValues[], int lo, int hi ) {

		resetComparisonCount();
		bp.setMaxValue(maxVal);
		for (int i=lo;i<hi;i++) bp.setBarValue(i-lo,intValues[i]);
		if ( len > 1 ) {
			int mid = (lo+hi)/2;
			left.setInput(maxVal,intValues,lo,mid);
			right.setInput(maxVal,intValues,mid,hi);
		}

		if ( parent == null ) {
			rewindMaxVal = maxVal;
			rewindIntValues = intValues;
			rewindLo = lo;
			rewindHi = hi;
		}
	}

	public synchronized void rewindInput() {
		setInput(rewindMaxVal,rewindIntValues,rewindLo,rewindHi);
		resetComparisonCount();
	}

	synchronized void sort(boolean pauseAtBeginning)
	throws InterruptedException, KillSortException {
		int L,R,w;
		int value;
		int mid = len/2;

		if ( pauseAtBeginning ) {
			ticker.pause();
		}
			
		resetComparisonCount();
		if ( parent != null ) {
			for (int i=0;i<len;i++) {
				parent.setBarInvisible(offset+i);
				setBarUnsorted(i);
			}
		}

		if ( left != null ) { // And therefore right != null.
			left.sort(true);
			//incrementCount(left.getComparisonCount());
			right.sort(true);
			//incrementCount(right.getComparisonCount());

			// Now merge them
			L = R = 0;
			w = 0;
			while ( L < mid || R < len-mid ) {
				if ( L == mid ) {
					right.setBarInvisible(R);
					value = right.getBarValue(R);
					R++;
				} else if ( R == len-mid ) {
					left.setBarInvisible(L);
					value = left.getBarValue(L);
					L++;
				} else {
					incrementCount();
					left.setBarMergeHead(L);
					right.setBarMergeHead(R);
					// Delay here
					ticker.pause();
					int LH = left.getBarValue(L);
					int RH = right.getBarValue(R);
					if ( LH <= RH ) {
						left.setBarInvisible(L);
						value = left.getBarValue(L);
						L++;
					} else {
						right.setBarInvisible(R);
						value = right.getBarValue(R);
						R++;
					}
				}
				bp.setBarValue(w,value);
				bp.setBarSorted(w);
				w++;
			}
		}
	}

	public int getBarValue(int i) {
		return bp.getBarValue(i);
	}

	public void setBarInvisibleAll() {
		for (int i=0;i<len;i++) setBarInvisible(i);
	}
	
	public void setBarInvisible(int i) {
		bp.setBarInvisible(i);
	}
	
	public void setBarUnsortedAll() {
		for (int i=0;i<len;i++) setBarUnsorted(i);
	}
	
	public void setBarUnsorted(int i) {
		bp.setBarUnsorted(i);
	}

	public void setBarSorted(int i) {
		bp.setBarSorted(i);
	}

	public void setBarMergeHead(int i) {
		bp.setBarMergeHead(i);
	}
	
	void incrementCount() {
		incrementCount(1);
	}
	
	void incrementCount(int c) {
		comparisonCount+=c;
		if ( comparisonCount >= comparisonUpperBound ) 
			throw new RuntimeException();	// i.e. this can't happen.
		CCLabel.incrementCount(c);
		if ( parent != null ) parent.incrementCount(c);
	}

	void resetComparisonCount() {
		comparisonCount = 0;
		CCLabel.resetCount();
	}

	public int getComparisonCount() {
		return comparisonCount;
	}

	// What to do when we're exposed.
	public void paint(Graphics g) {
		update(g);
	}

	public void update(Graphics g) {
		Dimension d = size();
//		g.setColor(getBackground());
//		g.fillRect(0,0,d.width-1,d.height-1);
		if ( isActive ) {
			g.setColor(getForeground());
		} else {
			g.setColor(getBackground());
		}
		g.drawRect(0,0,d.width-1,d.height-1);
	}

	public Insets insets() {
		return theseInsets;
	}

	public void rootUnsortedOnly() {
		if ( parent == null ) {
			setBarUnsortedAll();
		} else {
			setBarInvisibleAll();
		}
		if ( left != null ) {
			left.rootUnsortedOnly();
			right.rootUnsortedOnly();
		}
		comparisonCount = 0;
	}

	public void setActive( boolean b ) { 
		isActive = b;
		repaint();
	}
	public int getComparisonUpperBound() {
		return comparisonUpperBound;
	}
}


class Sorter extends Thread {
	MergeSortApp msa;
	MergeSortActivation msac;

	public Sorter(MergeSortApp msa, MergeSortActivation msac) {
		this.msa = msa;
		this.msac = msac;
	}


	public void run() {
		while ( true ) {
			try {
				msa.ticker.waitForGo();
				msac.sort(false);
				msa.doneSorting();
			} catch (InterruptedException e) {
			} catch (KillSortException e) {
				msa.ticker.stopForNow();	// Go from Killed to Stopped.
			}
		}
	}
}
