/*
 * Copyright (C) 1998  Ralf Wiebicke
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package de.rw7;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Checkbox;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Panel;
import java.awt.Scrollbar;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

abstract class QueueServer
	extends Panel
	implements Runnable, ItemListener, AdjustmentListener
{

	protected String title;
	protected Queue queue;
	protected boolean running;
	protected int waiting;
	protected int maxwait = 0;

	protected Thread thread;

	private Checkbox cb;

	QueueServer(String title, Queue queue, boolean running, int waiting)
	{
		super(new BorderLayout());

		this.title = title;
		this.queue = queue;
		this.running = running;
		this.waiting = waiting;

		cb = new Checkbox(title, running);
		cb.addItemListener(this);
		cb.setLabel(buildTitle());
		add(cb, "North");

		Scrollbar sb = new Scrollbar(Scrollbar.HORIZONTAL, waiting, 100, 0, 1000);
		sb.addAdjustmentListener(this);
		add(sb, "South");

		thread = new Thread(this);
		thread.setDaemon(true);
		thread.setPriority(thread.MIN_PRIORITY);
		thread.start();
	}

	private String buildTitle()
	{
		return (
			title
				+ " ("
				+ String.valueOf(waiting)
				+ "ms, "
				+ String.valueOf(maxwait)
				+ ")");
	}

	public void itemStateChanged(ItemEvent e)
	{
		switch (e.getStateChange())
		{
			case ItemEvent.SELECTED :
				running = true;
				thread.resume();
				break;
			case ItemEvent.DESELECTED :
				running = false;
				break;
			default :
				throw (new IllegalArgumentException());
		}
	}

	public void adjustmentValueChanged(AdjustmentEvent e)
	{
		waiting = e.getValue();
		e.getAdjustable().setValue(waiting);
		cb.setLabel(buildTitle());
	}

	void stop()
	{
		if (thread != null)
		{
			thread.stop();
			thread = null;
		}
	}

	public void run()
	{
		try
		{
			while (true)
			{
				if (!running)
					thread.suspend();
				int n = runQueue();
				if (n > maxwait)
				{
					maxwait = n;
					cb.setLabel(buildTitle());
				}
			}
		}
		catch (java.lang.InterruptedException e)
		{
			System.err.println("interrupt");
		}
	}

	abstract int runQueue() throws java.lang.InterruptedException;

} // class QueueServer

class Producer extends QueueServer

{
	Producer(Queue queue, boolean running)
	{
		super("Producer", queue, running, 100);
	}

	int runQueue() throws java.lang.InterruptedException
	{
		Thread.sleep(waiting);
		return queue.in();
	}

}

class Consumer extends QueueServer
{
	Consumer(Queue queue, boolean running)
	{
		super("Consumer", queue, running, 120);
	}

	int runQueue() throws java.lang.InterruptedException
	{
		Thread.sleep(waiting);
		return queue.out();
	}

}

class Queue
{
	int capacity, number = 0;

	Queue(int capacity)
	{
		this.capacity = capacity;
	}

	synchronized int in() throws java.lang.InterruptedException
	{
		int n;
		for (n = 0; isFull(); n++)
			wait();
		number++;
		notifyAll();

		if (component != null)
			component.repaint();
		return n;
	}

	synchronized int out() throws java.lang.InterruptedException
	{
		int n;
		for (n = 0; isEmpty(); n++)
			wait();
		number--;
		notifyAll();

		if (component != null)
			component.repaint();
		return n;
	}

	private final boolean isFull()
	{
		return (number == capacity);
	}

	private final boolean isEmpty()
	{
		return (number == 0);
	}

	private Component component;
	public Component getComponent()
	{
		if (component != null)
			return component;

		component = new Canvas()
		{
			private int lastvalue;
			private Dimension d;
			private int zoom = 1;

			public void paint(Graphics g)
			{
				d = getSize();
				zoom = d.width / capacity;
				int val = zoom * number;
				int cap = zoom * capacity;
				g.fillRect(0, 0, val, d.height);
				g.clearRect(val, 0, cap - val, d.height);
				g.fillRect(cap, 0, d.width - cap, d.height);
				lastvalue = val;
			}

			public void update(Graphics g)
			{
				int val = zoom * number;
				if (val > lastvalue)
					g.fillRect(lastvalue, 0, val - lastvalue, d.height);
				else
					g.clearRect(val, 0, lastvalue - val, d.height);
				lastvalue = val;
			}
		};

		return component;
	} // Queue.getComponent

} // class Queue

public class Concurrent extends java.applet.Applet
{
	Queue q;

	public void init()
	{
		setLayout(new BorderLayout(2, 2));
		q = new Queue(70);

		Panel P = new Panel();
		P.setLayout(new GridLayout(0, 1, 2, 2));
		P.add(new Producer(q, true));
		P.add(new Producer(q, false));
		P.add(new Producer(q, false));
		add(P, "North");

		P = new Panel();
		P.setLayout(new GridLayout(0, 1, 2, 2));
		P.add(new Consumer(q, true));
		P.add(new Consumer(q, false));
		P.add(new Consumer(q, false));
		add(P, "South");

		add(q.getComponent(), "Center");
	}

}
