/*
@author  j.n.magee 14/11/96
*/
import java.awt.*;
import java.applet.*;

/*********************BUFFER*****************************/

class Buffer {

    private protected Object[] buf;
    private protected int in = 0;
    private protected int out= 0;
    private protected int count= 0;
    private protected int size;

    Buffer(int size) {
        this.size = size;
        buf = new Object[size];
    }

    synchronized public void put(Object o) {
        while (count==size) {
            try {wait();} catch(InterruptedException e){}
        }
        buf[in] = o;
        ++count;
        in=(in+1) % size;
        notify();
    }

    synchronized public Object get() {
        while (count==0) {
            try {wait();} catch(InterruptedException e){}
        }
        Object o =buf[out];
        buf[out]=null;
        --count;
        out=(out+1) % size;
        notify();
        return (o);
    }
}

/*******************PRODUCER************************/

class Producer implements Runnable {

    Buffer buf_;
    String alphabet= "abcdefghijklmnopqrstuvwxyz";

    Producer(Buffer b) {
        buf_ = b;
    }

    public void run() {
        int ai = 0;
        while(true) {
            buf_.put(new Character(alphabet.charAt(ai)));
            ai=(ai+1) % alphabet.length();
            for(int i=0;i<60; i++)
                DisplayThread.rotate();
        }
    }
}

/********************CONSUMER*******************************/

class Consumer implements Runnable {

    Buffer buf_;

    Consumer(Buffer b) {
        buf_ = b;
    }

    public void run() {
        while(true) {
            for(int i=0;i<30; i++)
                DisplayThread.rotate();
            Character c = (Character)buf_.get();
            for(int i=0;i<30; i++)
                DisplayThread.rotate();

        }
    }
}

/****************************APPLET**************************/

public class BoundedBuffer extends Applet {


    Button startA_;
    Button stopA_;
    Button startB_;
    Button stopB_;

    DisplayThread A_;
    DisplayThread B_;

    public void init() {
        super.init();
        // Set up Buttons
        this.setFont(new Font("Helvetica",Font.BOLD,14));
        Panel p1 = new Panel();
        p1.add(startA_=new Button("Start Producer"));
        p1.add(stopA_=new Button("Stop Producer"));
        p1.add(startB_=new Button("Start Consumer"));
        p1.add(stopB_=new Button("Stop Consumer"));
        // Set up Display
        Panel p2 = new Panel();
        GraphicCanvas g1 = new GraphicCanvas("Producer",Color.blue);
        GraphicCanvas g2 = new GraphicCanvas("Consumer",Color.yellow);
        BufferCanvas b1 = new BufferCanvas("Buffer",5);
        p2.add(g1);
        p2.add(b1);
        p2.add(g2);
        // Arrange Applet display
        setLayout(new BorderLayout());
        add("Center",p2);
        add("South",p1);
        // Create Buffer
        DisplayBuffer b = new DisplayBuffer(b1,5);
        // Create Thread
        A_= new DisplayThread(g1,new Producer(b),100);
        B_= new DisplayThread(g2,new Consumer(b),100);
        A_.start();
        B_.start();
    }

    public void start() {
        super.start();
    }

    public void stop() {
        A_.passivate();
        B_.passivate();
    }

    public void destroy() {
        A_.stop();
        B_.stop();
    }


    public boolean handleEvent(Event event) {
        if (event.id != event.ACTION_EVENT) {
            return super.handleEvent(event);
        } else if(event.target==startA_) {
            A_.activate();
            return true;
        } else if (event.target==stopA_) {
            A_.passivate();
            return true;
        } else if(event.target==startB_) {
            B_.activate();
            return true;
        } else if (event.target==stopB_) {
            B_.passivate();
            return true;
      } else
            return super.handleEvent(event);
    }

}

/**************************************************************/

class DisplayBuffer extends Buffer {
    BufferCanvas disp_;

    DisplayBuffer(BufferCanvas disp,int size) {
        super(size);
        disp_ = disp;
    }

    private void display() {
        char[] tmp = new char[size];
        for (int i=0; i<size ; i++) {
            if (buf[i] != null)
                tmp[i] = ((Character)buf[i]).charValue();
            else
                tmp[i] = ' ';
        }
        disp_.setvalue(tmp,in,out);
    }

    synchronized public void put(Object o) {
        super.put(o);
        display();
        try{Thread.sleep(400);} catch (InterruptedException e) {}
    }

    synchronized public Object get() {
        Object o = super.get();
        display();
        return (o);
    }

 }

/**************************************************************/

class BufferCanvas extends Canvas {
    String title_;
    int slots_;
    int in_=0;
    int out_=0;
    char[] buf_ = {' ',' ',' ',' ',' '};

    Font f1 = new Font("Helvetica",Font.ITALIC+Font.BOLD,24);
    Font f2 = new Font("TimesRoman",Font.BOLD,36);

    BufferCanvas(String title, int slots) {
        super();
        title_=title;
        slots_=slots;
        resize(20+50*slots_,150);
        setBackground(Color.cyan);
  	}

    public void setvalue(char[] buf,int in, int out){
        buf_=buf;
        in_=in;
        out_=out;
        repaint();
    }

    public void paint(Graphics g) {
        update(g);
    }

    Image offscreen;
    Dimension offscreensize;
    Graphics offgraphics;

    public synchronized void update(Graphics g){
        Dimension d = size();
	    if ((offscreen == null) || (d.width != offscreensize.width)
	                            || (d.height != offscreensize.height)) {
	        offscreen = createImage(d.width, d.height);
	        offscreensize = d;
	        offgraphics = offscreen.getGraphics();
	        offgraphics.setFont(getFont());
	    }

	    offgraphics.setColor(getBackground());
	    offgraphics.fillRect(0, 0, d.width, d.height);

         // Display the title
        offgraphics.setColor(Color.black);
        offgraphics.setFont(f1);
        FontMetrics fm = offgraphics.getFontMetrics();
        int w = fm.stringWidth(title_);
        int h = fm.getHeight();
        int x = (size().width - w)/2;
        int y = h;
        offgraphics.drawString(title_, x, y);
        offgraphics.drawLine(x,y+3,x+w,y+3);
        // Buffer Boxes
        y = size().height/2 - 15;
        offgraphics.setColor(Color.white);
        offgraphics.fillRect(10,y,50*slots_,50);
        offgraphics.setColor(Color.black);
        offgraphics.setFont(f2);
        for(int i=0; i<slots_; i++) {
            offgraphics.drawRect(10+50*i,y,50,50);
            offgraphics.drawChars(buf_,i,1,25+50*i,y+35);
        }
        //Input and output Pointers
        offgraphics.setColor(Color.blue);
        offgraphics.fillOval(35+50*in_,y-20,15,15);
        offgraphics.setColor(Color.yellow);
        offgraphics.fillOval(35+50*out_,y+55,15,15);
        g.drawImage(offscreen, 0, 0, null);
    }
}


/**************************************************************/


class DisplayThread extends Thread {

    GraphicCanvas display_;
    boolean suspended = true;
    int angle_=0;
    int rate_;
    final static int step = 6;
    Runnable target_;

    DisplayThread(GraphicCanvas g, Runnable target, int rate) {
        display_ = g;
        display_.setcolor(Color.red);
        target_=target;
        rate_=rate;
    }

    synchronized void mysuspend() {
        while (suspended)
            try {wait();} catch (InterruptedException e) {}
    }

    public void passivate() {
        if (!suspended) {
            suspended = true;
            display_.setcolor(Color.red);
           }
    }

    public void activate() {
        if (suspended) {
            suspended = false;
            display_.setcolor(Color.green);
            synchronized(this) {notify();}
        }
    }

    public static void rotate() {
        DisplayThread d = (DisplayThread)Thread.currentThread();
        d.mysuspend();
        d.angle_=(d.angle_+step)%360;
        d.display_.setvalue(d.angle_);
        try {Thread.sleep(d.rate_); } catch (InterruptedException e){}
    }

    public void run() {
          mysuspend();
          target_.run();
     }
 }


/********************************************************/

class GraphicCanvas extends Canvas {
    int value_ = 0;
    String title_;
    Color arcColor_;

    Font f1 = new Font("Times",Font.ITALIC+Font.BOLD,24);

    GraphicCanvas(String title,Color arc) {
        super();
        title_=title;
        arcColor_ = arc;
        resize(150,150);
  	}

    public void setcolor(Color c){
        setBackground(c);
        repaint();
    }

    public void setvalue(int newval){
        value_ = newval;
        repaint();
    }

    public void paint(Graphics g){
        update(g);
    }

    Image offscreen;
    Dimension offscreensize;
    Graphics offgraphics;

    public synchronized void update(Graphics g){
        Dimension d = size();
	    if ((offscreen == null) || (d.width != offscreensize.width)
	                            || (d.height != offscreensize.height)) {
	        offscreen = createImage(d.width, d.height);
	        offscreensize = d;
	        offgraphics = offscreen.getGraphics();
	        offgraphics.setFont(getFont());
	    }

	    offgraphics.setColor(getBackground());
	    offgraphics.fillRect(0, 0, d.width, d.height);

             // Display the title
         offgraphics.setColor(Color.black);
         offgraphics.setFont(f1);
         FontMetrics fm = offgraphics.getFontMetrics();
         int w = fm.stringWidth(title_);
         int h = fm.getHeight();
         int x = (size().width - w)/2;
         int y = h+10;
         offgraphics.drawString(title_, x, y);
         offgraphics.drawLine(x,y+3,x+w,y+3);
         // Display the arc
         offgraphics.setColor(arcColor_);
         offgraphics.fillArc(50,50,90,90,0,value_);
         g.drawImage(offscreen, 0, 0, null);
    }
}

