// Pizza Producer/Consumer Simulation to Demonstrate Threads
// By Greg Murray
// Date: version 2 completed 8/27/96
//

import java.awt.*;
import java.applet.Applet;


public class pizzaDemo2 extends Applet implements Runnable {
  // Declare varribles/objects
   protected MediaTracker tracker;  
   private Thread pizzaThread= null;
   private PizzaStorage pizzastorage;
   private int number, pizzasOnOrder, total;
   private boolean orderMade, deliveryMade, pizzaMade;
   Thread consumer, producer;
   Image house, pizza, man, man2, tree, tree2, sun, usagi;

//The following function was modified from the pause funciton presented
//in Java in 21 Days page 204 which was written by Laura Lemay 

void pause(int atime){
  try{
  pizzaThread.sleep(atime);
 } catch(InterruptedException e){}
}

public void paint(Graphics g){

   int loop;
   deliveryMade = pizzastorage.getDeliveryMade();
   this.setBackground(Color.white);
   g.drawImage(house, 400,150, this);
   g.drawImage(pizza, 10,10, this);
   g.drawImage(sun,420,20,this);
   g.drawImage(tree, 150,50,this);
   g.drawImage(usagi, 355,250,this);
   g.drawImage(tree2, 250,100,this);
  
   if (deliveryMade){
      // Set Color
      g.setColor(Color.white);
      g.fillRect(100,100,25,23);  // exit the window

      // down animation
      for(loop = 131; loop <= 200; loop++){
           g.drawImage(man,75,loop, this);
          }

      // across animation
      for(loop = 75; loop <= 375; loop++){
         g.drawImage(man,loop,200, this);
         g.fillRect(loop-1,200,1,50);
          }

      // text animation
      g.setColor(Color.red);
      g.drawString("Pizza",loop,180);
      pause(2000);
      g.setColor(Color.white);
      g.fillRect(loop,165,50,25);
      g.setColor(Color.red);
      g.drawString("Thank You",380,165);
      pause(2000);
      g.setColor(Color.white);
      g.fillRect(380,145,65,25);

      // return across animation
      for(loop = 375; loop >= 75; loop--){
         g.drawImage(man2,loop,200, this);
         g.fillRect(loop+30,200,1,50);
          }
      // retun up animation
      for(loop = 200; loop >= 131; loop--){
           g.drawImage(man2,75,loop, this);
         }
      g.fillRect(75,loop,40,50);
      g.drawImage(pizza, 10,10, this);
      }
 }

public void run(){   // pizzaThread

 while(pizzaThread!=null){

 try{
      pizzaThread.sleep(1000);
      number = pizzastorage.get();
      total = pizzastorage.getTotalPizzasSold();
      deliveryMade = pizzastorage.getDeliveryMade();
      orderMade = pizzastorage.getOrderMade();
      pizzasOnOrder = pizzastorage.getPizzasOnOrder();
      pizzaMade = pizzastorage.getPizzaMade();

      if (pizzaMade)    // notify that a Pizza was completed
         {
          getAppletContext().showStatus("A Pizza Is Finished");
          pause(1600);
          pizzastorage.handlePizzaMade(false);
         }

      if (orderMade)
         {
          getAppletContext().showStatus("An Order was Made");
          pause(1600);
          pizzastorage.handleOrderMade(false);
         }



      if (deliveryMade)  //Notify if delivery is made and do animation
         {
          repaint();  //go do the animation
          getAppletContext().showStatus("A Delivery is Made");
          pause(1600);
          pizzastorage.handleDeliveryMade(false); //delivery done
         }


      getAppletContext().showStatus("Unsold Pizzas " + this.number
            + "          Pizzas on Order  " + pizzasOnOrder
            + "          Total Pizzas Sold  " + total);
   
         
    } catch(InterruptedException e) {
               }
   }
}


public void start (){  // start up all the threads when pizzaThread is null
           
    if (pizzaThread==null)
      {
    	 tracker = new MediaTracker(this);
       PizzaStorage p= new PizzaStorage();
       pizzastorage = p;
       producer = new MyProducer(p);
       pizzaThread = new Thread(this);
       consumer = new MyConsumer(p);

       // load the images

       house = getImage(getCodeBase(), "images/house.gif");
       pizza = getImage(getCodeBase(), "images/pizza.gif");
       man = getImage(getCodeBase(), "images/man.gif");
       man2 = getImage(getCodeBase(),"images/man2.gif");
 
       // This should help those of you on Unix to see the men

       tracker.addImage(man, 1);
       tracker.addImage(man2,2);  
       tree = getImage(getCodeBase(), "images/tree.gif");
       tree2 = getImage(getCodeBase(), "images/tree2.gif");

	 try {
            tracker.waitForID(1);
           } 
	 catch (InterruptedException e){}
       try {
            tracker.waitForID(2);
          } 
	 catch (InterruptedException e){}
	 
	 sun = getImage(getCodeBase(), "images/sun.gif");
       usagi = getImage(getCodeBase(), "images/usagi.gif");

       // start the threads
       consumer.start();
       pizzaThread.start();
       producer.start();
      }
}


public void stop(){

  // shut down all the threads
  pizzaThread.stop();
  pizzaThread = null;
  producer.stop();
  producer = null;
  consumer.stop();
  consumer = null;
}
}// end PizzaDemo

//*************************************************************************



class MyProducer extends Thread {
 
  private PizzaStorage pizzastorage;
  private int number, pizzasOnOrder, total;
 

public MyProducer(PizzaStorage p){
  pizzastorage=p;
}// end constructor

public void  run (){     //producer Thread
     
  while(this!=null)  
    {
      try{
            this.sleep(30000); // making a pizza
            this.number = pizzastorage.get();
            this.total = pizzastorage.getTotalPizzasSold();
            this.pizzasOnOrder = pizzastorage.getPizzasOnOrder();
            this.pizzastorage.handlePizzaMade(true);
            System.out.println("A Pizza is completed");

            // Handle those waiting for pizza

            if (pizzasOnOrder>=1){
                  pizzastorage.handleTotalPizzasSold(total + 1);
                  pizzastorage.handlePizzasOnOrder(pizzasOnOrder-1);
                  System.out.println("Consumer gets Ordered Pizza");
                  pizzastorage.handleDeliveryMade(true); //notify to deliver
              }
            else{
                  pizzastorage.put(this.number + 1);  // add pizza
                }
         } catch(InterruptedException e) {
               }
    }  //end while loop   
     
} // end Producer run

 
} // end Producer class

//************************************************************************

class MyConsumer extends Thread {

  int pizzaMade;
  private PizzaStorage pizzastorage;
  private int number, pizzasOnOrder, total;

  //Constructor
public MyConsumer (PizzaStorage p){
        pizzastorage = p;
       }

public void  run (){
      
    while(this!=null)  
      {
       try{
            this.sleep((int)(Math.random()*55000));// randomize orders
            this.number = pizzastorage.get();
            this.pizzasOnOrder = pizzastorage.getPizzasOnOrder();
            this.total = pizzastorage.getTotalPizzasSold();
            pizzastorage.handleOrderMade(true);

            //if there are pizza give them a pizza
            if (this.number>=1) 
               {
                  pizzastorage.handleTotalPizzasSold(total + 1);
                  pizzastorage.put(this.number-1); // reduce pizza in storage
                  pizzastorage.handleDeliveryMade(true); //notify for delivery
                  System.out.println("Consumer gets an Unsold pizza");
               }
            //if there are no pizzas put on order
            else if (this.number<=1){
               pizzastorage.handlePizzasOnOrder(this.pizzasOnOrder+1);//increase 
               System.out.println("No Pizzas -- Put On Order  ");
              }
          }   catch(InterruptedException e) {
               }
       } // end while loop
} // end Consumer run 
} // end class Consumer Class

//*************************************************************************

//This is the monitor
//The concept for this monitor was adapted from  an example in a tutorial
//Located on the internet at:
//http://w6.infosys.tuwien.ac.at/java...l/java/threads/sysnchronization.html
//The code was adapted here as a monitor for my two threads

class PizzaStorage {

  private int safePizza;
  private boolean orderMade = false;
  private boolean deliveryMade = false;
  private boolean pizzaMade = false;
  private int pizzasOnOrder;
  private int totalPizzasSold;
 
//manipulate pizzas available
public synchronized void put(int invalue) {
  safePizza = invalue;
  notify();
}

//retrieve pizzas that are in storage
public synchronized int get() {
  notify();
  return safePizza;
}

//handle the pizzas on order
public synchronized void handlePizzasOnOrder (int invalue) {
  pizzasOnOrder = invalue;
  notify(); 
}

//retrieve the pizzas on order
public synchronized int getPizzasOnOrder() {
  notify();
  return pizzasOnOrder;
}

//retrieve the total pizzas sold
public synchronized int getTotalPizzasSold() {
  notify();
  return totalPizzasSold;
}

//allow manipulation of the total pizzas sold
public synchronized void handleTotalPizzasSold (int invalue) {
  totalPizzasSold = invalue;
  notify();
}

//allow for the manipulation of order made for notification: true = order made
public synchronized void handleOrderMade (boolean invalue) {
  orderMade = invalue;
  notify();
}

//notify if an order has been made: true  = order to be notified
public synchronized boolean getOrderMade() {
  notify();
  return orderMade;
}

//all the delivery made to be manipulated: true = an order is made
public synchronized void handleDeliveryMade (boolean invalue) {
  deliveryMade = invalue;
  notify();
}

//notify if a delivery is to be made: true = delivery to be made
public synchronized boolean getDeliveryMade() {
  notify();
  return deliveryMade;
}

//allow the user to manipulate wether or not a pizza order is made: true = notify
public synchronized void handlePizzaMade (boolean invalue) {
  pizzaMade = invalue;
  notify();
}

//notify if a pizza made notification is needed: true = notify
public synchronized boolean getPizzaMade() {
  notify();
  return pizzaMade;
}
}// end PizzaStorage Class