public class ProdCons extends Thread
{
  public static void main(String args[])
  {
    Thread prodcons1= new ProdCons(4, 4); prodcons1.start();
    Thread prodcons2= new ProdCons(20, 10); prodcons2.start();
    Thread prodcons3= new ProdCons(20, 10); prodcons3.start();
    Thread prodcons4= new ProdCons(20, 10); prodcons4.start();

    try
    {
      prodcons1.join();
      prodcons2.join();
      prodcons3.join();
      prodcons4.join();
    }
    catch ( InterruptedException I ) { throw new Error("Interrupcion"); }
  }

  public ProdCons(int prodCount, int consCount)
  {
    Port itemPort= new Port();
    Port consPort= new Port();
    Port prodPort= new Port();

    int consSum= 0;
    int prodSum= 0;

    Thread prodArray[]= new Thread[prodCount];
    Thread consArray[]= new Thread[consCount];

    int i;
    for (i=0; i<prodCount; i++)
    {
      prodArray[i]= new Producer(itemPort, prodPort);
      prodArray[i].start();
    }

    for (i=0; i<consCount; i++)
    {
      consArray[i]= new Consumer(itemPort, consPort);
      consArray[i].start();
    }

    for (i=0; i<prodCount; i++)
    {
       CountMsg countMsg= (CountMsg)prodPort.receive();
       prodSum+= countMsg.count;
       Thread sender= countMsg.getSender();
       countMsg.reply();
       try { sender.join(); }
         catch ( InterruptedException I ) { throw new Error("Interrupcion"); }
    }

    for (i=0; i<consCount; i++)
    {
       new Msg().send(itemPort);
       CountMsg countMsg= (CountMsg)consPort.receive();
       consSum+= countMsg.count;
       Thread sender= countMsg.getSender();
       countMsg.reply();
       try { sender.join(); }
         catch ( InterruptedException I ) { throw new Error("Interrupcion"); }
    }

    if (prodSum!=consSum) System.out.println("Error en la suma!!");
  }
}

class Producer extends Thread
{
  Port itemPort, sumPort;

  public Producer(Port itemPort, Port sumPort)
  {
    this.itemPort= itemPort;
    this.sumPort= sumPort;
  }

  public void run()
  {
    CountMsg countMsg= new CountMsg();
    int msgCount= (int)Math.round(Math.random()*100);
    int sum= 0;
    int i;
    for(i=0; i<msgCount; i++)
    {
      countMsg.count= (int)Math.round(Math.random()*100);
      // System.out.println("Envio "+countMsg.count);
      sum+= countMsg.count;
      countMsg.send(itemPort);
    }

    countMsg.count= sum;
    // System.out.println("Envio suma "+countMsg.count);
    countMsg.send(sumPort);
    // System.out.println("Envio termina");
    System.out.println("Productor envia "+msgCount+" mensajes");
  }
}

class Consumer extends Thread
{
  Port itemPort, sumPort;

  public Consumer(Port itemPort, Port sumPort)
  {
    this.itemPort= itemPort;
    this.sumPort= sumPort;
  }

  public void run()
  {
    int sum= 0;
    boolean end= false;

    // System.out.println("Consumidor parte");
    while (!end)
    {
      Msg msg= itemPort.receive();
      if (msg instanceof CountMsg) sum+= ((CountMsg)msg).count;
      else end= true;
      // System.out.println("Recibi sum= "+ sum);
      msg.reply();
    }

    CountMsg countMsg= new CountMsg();
    countMsg.count= sum;
    // System.out.println("Consumidor envia sum= "+ sum);
    countMsg.send(sumPort);
    // System.out.println("Consumidor termina");
  }
}

class CountMsg extends Msg
{
  int count;
}
