/**
 Hallo Leute.

Ich moechte das Consumer/Producer Problem anhand eines 
Supermarktes simulieren, also die Kasse ist der Consumer 
und der KundenGen der Producer, welcher Kunden - Objekte erzeugt.

Das Ganze funktioniert quasi auch, mein Problem ist nur das es 
so "abgehackt" laeuft. Ich mein damit, z.B. dass der KundenGen 
sehr lange laeuft und durchgehend 150 Kunden erzeugt, dann laeuft 
die Kasse wieder lange und nimmt 100 Kunden weg ohne dass 
dazwischen der KundenGen wieder welche hinzufuegen kann.

Meine Frage ist daher: Wie kann ich es aendern, dass die Threads 
jeweils nicht so lange laufen und sich oefter abwechseln, 
also zum Beispiel 4 Kunden hinzukommen, 1 geloescht wird, 
   2 hinzukommen, 3 geloescht werden usw.
*/

import java.util.LinkedList;
import java.util.logging.*;

public class KundeMain {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        LinkedList<Kunde> warteschlange = new LinkedList();
        KundenGen producer = new KundenGen(warteschlange);
        Kasse consumer = new Kasse(warteschlange);
    }

}

class Kunde {
    
    private static int lastID = 0;
    private int id;
    private float warenpreis;

    public Kunde() {
        this.id = this.nextID();
        this.einkaufen();
    }
    public int nextID() {
        return (lastID++);
    }
    public void einkaufen() {
        this.warenpreis = (float)Math.random()*10;
    }
    public int getId() {
        return this.id;
    }
}

class KundenGen extends Thread {
    
    private LinkedList<Kunde> warteschlange = null;
    boolean ende = false;
    
    public KundenGen(LinkedList<Kunde> w) {
        this.warteschlange = w;
        this.start();
    }
    public void run() {
        while(true) {
            synchronized(warteschlange) {
                try {
                    Kunde k = new Kunde();
                    warteschlange.addFirst(k);
                    warteschlange.notifyAll(); 
//Um andere Threads aufzuwecken die auf diese Liste warten
                    System.out.println("Neuer Kunde " + k.getId() + 
                      "! Anzahl der Kunden: " + warteschlange.size());
                    
                    
                    double faktor = Math.random()*100;
                    int wartezeit = (int)(Math.floor(faktor));
                    this.sleep(wartezeit);
                    
                    if (ende) {
                        break;
                    }
                } catch (InterruptedException ex) {
                    Logger.getLogger(KundenGen.class.getName()).
                           log(Level.SEVERE, null, ex);
                }
            }
        }
    }
    public void beenden() {
        this.ende = true;
    }
}

class Kasse extends Thread {
    
    LinkedList<Kunde> warteschlange = null;
    boolean ende = false;
    
    public Kasse(LinkedList<Kunde> w) {
        this.warteschlange = w;
        this.start();
    }
    public void run() {
        while(true) {
            Kunde k = null;
            synchronized(warteschlange) {
                try {
                    while (warteschlange.size() == 0) {
                        try {
                            System.out.println("Kasse leer");
                            warteschlange.wait();
                        } catch (InterruptedException ex) {
                            System.out.
                              println("CONSUMER INTERRUPTED");
                        }
                    }
                    k = warteschlange.removeLast();
                    warteschlange.notifyAll();
                    int len = warteschlange.size();
                    System.out.println("Kunde " + k.getId() + 
                      "entfernt! Anzahl der Kunden " + len);
                    
                    double faktor = Math.random()*100;
                    int wartezeit = (int)(Math.floor(faktor));
                    this.sleep(wartezeit);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Kasse.class.getName()).
                         log(Level.SEVERE, null, ex);
                }
            }
            
            if(ende) break;
        }
    }
    public void beenden() {
        this.ende = true;
    }

}
