import javax.realtime.*;
import java.io.*;

/** 
 * G53SRP coursework 2009/10. Chris Greenhalgh, University of Nottingham.
 */
public class DangerousAnimal
{
    static String FILENAME = "DangerousAnimalHistory.txt";
    static PrintWriter log;

    AbsoluteTime nextStrokeTime = null;
    AbsoluteTime lastStrokeTime = null;
    OneShotTimer noStrokeTimer;
    static RelativeTime maxStrokeDelay = new RelativeTime(100, 0);
    static RelativeTime strokeInterval = new RelativeTime(3000, 0);
    AsyncEvent growlEvent;
    OneShotTimer growlTimer;
    java.util.Random random;
    static RelativeTime minGrowlInterval = new RelativeTime(6000, 0);
    static RelativeTime maxGrowlInterval = new RelativeTime(13000, 0);
    static RelativeTime feedDeadline = new RelativeTime(5500, 0);
    static RelativeTime minGetFoodTime = new RelativeTime(2000, 0);
    static RelativeTime maxGetFoodTime = new RelativeTime(6000, 0);
    int feed;
    OneShotTimer noFeedTimer;
    /** cons 
     */
    public DangerousAnimal()
    {
	synchronized (DangerousAnimal.class)
	{
	    if (log != null)
	    {
		System.err.println("Do not make more than one dangerous animal!");
		log.println("Tried to make a second DangerousAnimal");
		end();
	    }

	    System.err.println("Logging to " + FILENAME);
	    try
	    {
		log = new PrintWriter(new FileWriter(FILENAME));
	    }
	    catch (IOException ioe)
	    {
		System.err.println("Error creating logfile " + FILENAME + ": " + ioe);
		System.exit(-1);
	    }
	}
	AbsoluteTime now = lastStrokeTime = Clock.getRealtimeClock().getTime();
	nextStrokeTime = now.add(strokeInterval);
	log("Creating DangerousAnimal " + this);
	noStrokeTimer = new OneShotTimer(nextStrokeTime, new NoStrokeHandler());
	growlEvent = new AsyncEvent();
	random = new java.util.Random();
	growlTimer = new OneShotTimer(getNextGrowlTime(), new GrowlHandler());
	noFeedTimer = new OneShotTimer(new RelativeTime(0,0), new NoFeedHandler());
	feed = 0;

	noStrokeTimer.start();
	growlTimer.start();
    }
    /** no stroke
     */
    static class NoStrokeHandler extends AsyncEventHandler
    {
	NoStrokeHandler() {
	    setDaemon(false);
	}
	public void handleAsyncEvent()
	{
	    log("ERROR: Stoke too late");
	    System.err.println("Stoke too late: you have been eaten!");
	    end();
	}
    }
    /** stroke 
     */
    public void stroke()
    {
	AbsoluteTime now = Clock.getRealtimeClock().getTime();
	RelativeTime elapsed = now.subtract(lastStrokeTime);
	log("Stroke after " + elapsed);
	System.err.println("stroke()");
	lastStrokeTime = now;
	nextStrokeTime = nextStrokeTime.add(strokeInterval);
	noStrokeTimer.reschedule(nextStrokeTime);
    }

    private RelativeTime getNextGrowlTime() {
	double alpha = random.nextDouble();
	RelativeTime delay = new RelativeTime((long)(minGrowlInterval.getMilliseconds()*alpha+
	    (1-alpha)*maxGrowlInterval.getMilliseconds()), 0);
	return delay;
    }
    /** get growl event 
     */
    public AsyncEvent getGrowlEvent()
    {
	return growlEvent;
    }
    /** growl
     */
    class GrowlHandler extends AsyncEventHandler
    {
	public void handleAsyncEvent()
	{
	    growlTimer.reschedule(getNextGrowlTime());
	    growlTimer.start();
	    log("Growl...");
	    System.err.println("Growl...");
	    growlEvent.fire();
	    noFeedTimer.reschedule(feedDeadline);
	    noFeedTimer.start();
	}
    }
    public DangerousAnimal.Food getFood() {
	feed++;
	try {
	    if (feed<3) {
    		log("getFood() [normal]...");
    		RealtimeThread.sleep(minGetFoodTime);
	    }
	    else {
    		log("getFood() [slow]...");
    		RealtimeThread.sleep(maxGetFoodTime);
	    }
	}
	catch (InterruptedException ie) {}
	log("gotFood!");
	return new Food();
    }
    /** food class
     */
    public static class Food {
	boolean eaten = false;
	private Food() {
	}
    }
    /** feed */
    public void feed(DangerousAnimal.Food food) {
	if (food.eaten) {
	    log("ERROR: this food has already been eaten");
	    end();
	}
	log("feed()");
	food.eaten = true;
	noFeedTimer.stop();
    }
    /** eat the person
     */
    class NoFeedHandler extends AsyncEventHandler
    {
	public void handleAsyncEvent()
	{
	    log("ERROR: the animal has not been fed and eats you!");
	    System.err.println("the animal has not been fed and eats you!");
	    end();
	}
    }
    public void runAway()
    {
	log("runAway()");
	System.err.println("You run away!");
	end();
    }
    /** end
     */
    static private void end()
    {
	log("End");
	log.close();
	System.exit(0);
    }
    static private void log(String s)
    {
	log.println(Clock.getRealtimeClock().getTime()+" "+s);
	log.flush();
    }
}

