// =========================================================
// Демонстрация синхронизации двух задач
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;

// =========================================================
// Основной класс аплета
// =========================================================
public class Synchro extends Applet
{
  // Ссылка на задачу рисования прямоугольников
  DrawRectangles m_DrawRectThread = null;

  // Ссылка на задачу, периодически разблокирующую задачу
  // рисования прямоугольников
  NotifyTask m_NotifyTaskThread = null;

	// -------------------------------------------------------
  // getAppletInfo
  // Метод, возвращающей строку информации об аплете
  // -------------------------------------------------------
	public String getAppletInfo()
	{
		return "Name: Synchro\r\n" +
      "Author: Alexandr Frolov\r\n" +
      "E-mail: frolov@glas.apc.org" +
      "WWW:    http://www.glasnet.ru/~frolov" +
      "Created with Microsoft Visual J++ Version 1.0";
	}

  // -------------------------------------------------------
  // paint
  // Метод paint, выполняющий рисование в окне аплета
  // -------------------------------------------------------
	public void paint(Graphics g)
	{
    // Определяем текущие размеры окна аплета
    Dimension dimAppWndDimension = size();
    
    // Выбираем в контекст отображения желтый цвет
    g.setColor(Color.yellow);
    
    // Закрашиваем внутреннюю область окна аплета
    g.fillRect(0, 0, 
      dimAppWndDimension.width  - 1, 
      dimAppWndDimension.height - 1);

    // Выбираем в контекст отображения черный цвет
    g.setColor(Color.black);

    // Рисуем рамку вокруг окна аплета
    g.drawRect(0, 0, 
      dimAppWndDimension.width  - 1, 
      dimAppWndDimension.height - 1);
	}

  // -------------------------------------------------------
  // start
  // Метод вызывается при первом отображении окна аплета
  // -------------------------------------------------------
	public void start()
	{
    if (m_DrawRectThread == null)
		{
      m_DrawRectThread = new DrawRectangles(this);
      m_DrawRectThread.start();
		}

    if (m_NotifyTaskThread == null)
		{
      // Создаем задачу, передавая ей ссылку на
      // задачу рисования прямоугольников, которую
      // необходимо периодически разблокировать
      m_NotifyTaskThread = new NotifyTask(m_DrawRectThread);
      m_NotifyTaskThread.start();
		}
	}
	
  // -------------------------------------------------------
  // stop
  // Метод вызывается, когда окно аплета исчезает с экрана
  // -------------------------------------------------------
	public void stop()
	{
		if (m_DrawRectThread != null)
		{
			m_DrawRectThread.stop();
			m_DrawRectThread = null;
		}

    if (m_NotifyTaskThread != null)
		{
			m_NotifyTaskThread.stop();
			m_NotifyTaskThread = null;
		}
	}
}

// =========================================================
// Класс задачи для рисования прямоугольников
// =========================================================
class DrawRectangles extends Thread
{
  // Контекст отображения окна аплета
  Graphics g;

  // Размеры окна аплета
  Dimension dimAppWndDimension;

  // -------------------------------------------------------
  // DrawRectangles
  // Конструктор класса DrawRectangles
  // -------------------------------------------------------
  public DrawRectangles(Applet Appl)
  {
    // Получаем и сохраняем контекст отображения
    g = Appl.getGraphics();

    // Определяем текущие размеры окна аплета
    dimAppWndDimension = Appl.size();
  }

	// -------------------------------------------------------
  // run
  // Метод, который работает в рамках отдельной задачи
  // Он рисует в окне аплета прямоугольники случайного
  // цвета, размера и расположения
  //
  // Этот метод должен быть определен как synchronized
  // -------------------------------------------------------
	public synchronized void run()
	{
    while (true)
		{
      int x, y, width, height;
      int rColor, gColor, bColor;
      
      // Выбираем случайным образом размеры
      // и расположение рисуемого прямоугольника
      x =      (int)(dimAppWndDimension.width * Math.random());
      y =      (int)(dimAppWndDimension.height * Math.random());
      width  = (int)(dimAppWndDimension.width * Math.random()) / 2;
      height = (int)(dimAppWndDimension.height * Math.random()) / 2;
      
      // Выбираем случайный цвет для рисования прямоугольника
      rColor = (int)(255 * Math.random());
      gColor = (int)(255 * Math.random());
      bColor = (int)(255 * Math.random());

      // Устанавливаем выбранный цвет в контексте отображения
      g.setColor(new Color(rColor, gColor, bColor));

      // Рисуем прямоугольник
      g.fillRect(x, y, width, height);

      // Переводим задачу в сотояние ожидания, в котором
      // она будет находиться до тех пор, пока не будет
      // разблокирована задачей класса NotifyTask
      try
			{
				Thread.wait();
			}
			catch (InterruptedException e)
			{
			}
		}
	}
}

// =========================================================
// Класс задачи для периодического разблокирования
// задачи рисования прямоугольников
// =========================================================
class NotifyTask extends Thread
{
  // Ссылка на задачу, которую необходимо разблокировать
  Thread STask;

  // -------------------------------------------------------
  // Конструктор класса NotifyTask
  // -------------------------------------------------------
  public NotifyTask(Thread SynchroTask)
  {
    // Сохраняем ссылку на задачу, которую необходимо
    // разблокировать
    STask = SynchroTask;
  }

  // -------------------------------------------------------
  // run
  // Метод, который работает в рамках отдельной задачи и
  // периодически разблокирует задачу STask
  // -------------------------------------------------------
	public void run()
	{
    while (true)
		{
      // Выполняем задержку на 30 миллисекунд
      try
			{
				Thread.sleep(30);
			}
			catch (InterruptedException e)
			{
  		}

      // Получаем объект STask в монопольное владение
      // и вызываем для него метод notify, 
      // разблокируя работу соотвестсвующей задачи
      synchronized(STask)
      {
        STask.notify();
      }
		}
	}
}

