package elaprendiz.estructuras;

import java.awt.Image;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Dimension;

import java.util.Vector;
import java.util.Observer;
import java.util.Observable;

import elaprendiz.graficos.imagenes.ImageFrame;
import elaprendiz.graficos.funciones.Funcion;



class FuncionHilo extends Thread {

  EstructuraLienzo canvas;

  FuncionHilo(EstructuraLienzo canvas) {
    super();

    this.canvas = canvas;
    setPriority(Thread.MIN_PRIORITY);

    start();
  }

  public void run() {
    while (true) 
      canvas.doFunctions();
  }
}


class BufferHilo extends Thread {

  EstructuraLienzo canvas;

  BufferHilo(EstructuraLienzo canvas) {
    super();

    this.canvas = canvas;
    setPriority(Thread.MIN_PRIORITY);

    start();
  }

  public void run() {
    while (true) 
      canvas.loadBuffer();
  }
}


class HiloAnimador extends Thread {

  final static int delay = 20;

  EstructuraLienzo canvas;

  HiloAnimador(EstructuraLienzo canvas) {
    super();

    this.canvas = canvas;
    setPriority(Thread.MIN_PRIORITY);

    start();
  }

  public void run() {
    while (true) {
      long startTime = System.currentTimeMillis() + delay;

      canvas.paintAnimation();

      try {
	sleep(Math.max(0, startTime - 
		       System.currentTimeMillis()));
      } catch (InterruptedException e) {
      }
    }
  }
}

/**
 * Basic form of an Animated Data Structure canvas.
 */
abstract public class EstructuraLienzo extends Canvas implements Observer {

  ///////////////////////////////////
  // Vector of functions to be per-
  // formed upon data structure nodes

  protected Vector functionVector = new Vector();

  protected int    nodeSize;

  ///////////////////////////////////
  // ImageFrame reference so that
  // image objects can be easily
  // created by Functions

  public ImageFrame frame;

  ///////////////////////////////////
  // Double buffering instance var's

  protected Dimension offDimension;
  protected Graphics  offGraphics;
  protected Image     offImage;

  ///////////////////////////////////
  // Vector to hold copys of the 
  // data structure nodes (each copy
  // is represented as a Vector).
  
  protected Vector copyVector = new Vector();

  protected boolean functionSuspended = true;
  protected boolean bufferLoaded      = false;
  protected boolean paintingDone      = true;

  protected Vector updateVector = new Vector();
  protected String updateString = "";

  protected FuncionHilo functionThread = new FuncionHilo(this);
  protected BufferHilo   bufferThread   = new BufferHilo(this);
  protected HiloAnimador animatorThread = new HiloAnimador(this);

  /**
   * Constructor.
   */
  public EstructuraLienzo(ImageFrame frame, int nodeSize) {
    this.frame    = frame;
    this.nodeSize = nodeSize;
  }

  ///////////////////////////////////////////////////////////////////////////
  // Add/Remove functions for EstructuraLienzo Vectors
  //
  //  functionVector
  //  nodeVector
  //
  // These Vectors are protected and should not be mutatable by outside objects
  // to insure proper functioning of animations.
  ///////////////////////////////////////////////////////////////////////////

  public synchronized void addFunction(Funcion function) {
    functionVector.addElement(function);

    functionSuspended = false;
    notifyAll();
  }

  public void removeFunction(Funcion function) {
    functionVector.removeElement(function);
  }

  public int numberFunctions() {
    return functionVector.size();
  }

  public int getNodeSize() {
    return nodeSize;
  }

  ///////////////////////////////////////////////////////////////////////////
  // Package access only functions
  //
  //  doFunctions
  //  clean
  //
  // These functions should be accessible only to DataStructure Applets. 
  // Separation has been maintained between image creation, which is the
  // Applet's domain, and image rendering, which is the EstructuraLienzo'
  // domain.
  //
  // Functions are meant to manipulate the appearance of the Structure-
  // Canvas (simulating a Data Structure) and thus are held by the Canvas.
  //
  // ImagesObjects (the items to be drawn on the canvas) are also held by
  // the Canvas; however, their creation depends upon the ImageFrame. 
  ///////////////////////////////////////////////////////////////////////////

  ///////////////////////////////////////////////////////
  // Perform functions held in functionVector. Remove 
  // function if it has completed, otherwise, perform 
  // function. Redraw canvas when completed.

  protected synchronized void doFunctions() {
    int i = 0;

    while (functionSuspended) {
      try {
	wait();
      } catch (InterruptedException e) {
      }
    }

    while (i < functionVector.size()) {

      ///////////////////////////////////
      // If function has completed then
      // remove it from functionVector.

      if ( ( (Funcion) functionVector.elementAt(i)).funcionTerminada())
	functionVector.removeElementAt(i);

      else {

	///////////////////////////////////
	// Otherwise, perform function and
	// move on to the next (if any).

	( (Funcion) functionVector.elementAt(i)).performFuncion();
	i++;

      }
    }

    cloneNodes();

    if (functionVector.size() == 0)
      functionSuspended = true;
  }

  abstract protected  void cloneNodes();

  abstract public  void loadBuffer();
  abstract public  void paintAnimation();

  public void update(Observable observed, Object arg) {
    if (arg instanceof String) {
      updateVector.addElement(arg);
    }
  }

}
