package edu.calpoly.csc480.Corral.Seer;

import java.io.*;
import java.util.*;

import com.bcurry.www.swing.*;

import edu.calpoly.csc480.Corral.Agent.*;
import edu.calpoly.csc480.Corral.Area.*;
import edu.calpoly.csc480.Corral.Main.*;

public abstract class BaseSeer implements Runnable
{
	public BaseSeer() {
		area = makeArea();
		rules = makeRules();
		view = makeView();
		
		area.setOwner(this);
		view.setDocument(this);
		areaClosed();

		start = pause = false;
		delay = 0;
	}
	
   protected void finalize() throws Throwable {
      start = pause = false;
      notify();

		if (thread != null) {
      	thread.join();
		}
   }
	
   final public boolean isRunning()				{return (start && !pause);}
	final public boolean isResting()				{return !isRunning();}

	final public boolean isStarted()				{return start;}
	final public boolean isPaused()				{return pause;}

	final public boolean isRunnable()			{return isAgentSelected();}
	final public boolean isOver()					{return over;}
	
	final public boolean isAgentSelected()		{return agentName != "";}
	
	final public BaseMain getOwner()				{return main;}
	final public BaseArea getArea()				{return area;}
	final public BaseRules getRules()			{return rules;}
	final public BaseSeerView getView()			{return view;}
	
	final public String getAgentName()			{return agentName;}
	final public String getLogFileName() {
		return view.getLogView().getFileName();
	}

	final public int getDelay()					{return delay;}
	final public int getMaximumDelay()			{return maximumDelay;}
	
	final public long getCycle()					{return cycle;}
	
	public void setOwner(BaseMain main) {
		this.main = main;
		view.setOwner(main);
	}

	public void areaOpened(InputStream in) throws IOException {
		stop();
		
		area.read(in);
		view.areaOpened();
	}
	
	public void areaClosed() {
		stop();
	
		area.close();
		view.areaClosed();

		agentUnselected();
	}

	public void agentSelected(String agentName) {
		BaseMeans means;
		
		stop();
		
		this.agentName = agentName;
		means = area.makeMeans(agentName);
		view.agentSelected(means);
	}
	
	public void agentUnselected() {
		stop();
		
		this.agentName = "";
		area.killMeans();
		view.agentUnselected();
	}

	public void setLogFileName(String logFileName)
    throws FileNotFoundException {
		stop();	
		view.getLogView().setFileName(logFileName);
	}
	
	public void setDelay(int delay) {
		this.delay = delay;
	}
	
	synchronized public void flip() {
		if (!start)			{play();}
		else if (pause)	{resume();}
		else					{pause();}
	}
	
	synchronized public void play() {
		if (!start) {
			cycle = 0;
			start = pause = true;
			over = false;
			initBeans();
			view.start();

			thread = new Thread(this);
			thread.setPriority(Thread.MIN_PRIORITY);
			thread.start();
		}

		resume();
	}
	
	synchronized public void pause() {
   	pause = true;	
	}
	
	synchronized public void resume() {
		pause = false;
		notify();
	}
	
	public void step() {
		play();
	      
      try {next();}
      catch(InterruptedException err) {}
      
      pause();
	
		if (isOver()) {
			BasicOptionPane.showError(
			 "It's Game Over!",
			 "Please insert two quarters.");
		}
	}
	
	synchronized public void stop() {
		start = pause = false;
		notify();

		view.stop();
	}

	public void over() {
		this.over = true;
	}
	
	public void run() {
      try {
         while (!thread.interrupted() && start == true) {
            next();
            thread.sleep(delay);
         }
      } catch(InterruptedException err) {err.printStackTrace(System.err);}

	   if (isOver()) {
	   	BasicOptionPane.showError(
	   	 "It's Game Over!",
	   	 "Please insert two quarters.");
	   }
   }
	
	static final protected int maximumDelay = 2000;
	
	protected BaseMain main;
	protected BaseArea area;
	protected BaseRules rules;
	protected BaseSeerView view;
	
	protected String agentName;
	
   protected Thread thread;
	
	protected boolean start, pause, over;
	protected int delay;
	protected long cycle;

	// FIX BWC:  Optimize this (try removing ".gc()", "wait()", etc)
   synchronized protected void next() throws InterruptedException {
      while (pause) {
         wait();
      }

      System.runFinalization();
      System.gc();

		cycle();
      view.next();

      if (++cycle < 0) {
         cycle = 0;
      }

      if (over) {
         end();
      }
   }
	
   synchronized protected void end() {
		stop();
	}

	abstract protected BaseArea makeArea();
	abstract protected BaseRules makeRules();
	abstract protected BaseSeerView makeView();

	abstract public void initBeans();

	abstract protected void cycle();
}