package elaprendiz.estructuras.funciones;

import elaprendiz.graficos.funciones.Funcion;

import java.awt.Point;

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


import elaprendiz.estructuras.nodos.ArbolNodo;
import elaprendiz.estructuras.LienzoBinario;

public class Insertar extends Funcion implements Observer {

  ////////////////////////////////////////
  // Reference to LienzoBinario used exten-
  // sively since insert performs does 
  // many modifications to the Binary Tree

  protected LienzoBinario canvas;

  ////////////////////////////////////////
  // Reference to new node created

  protected ArbolNodo nodoNuevo;

  ////////////////////////////////////////
  // References used to trapse down the
  // binary tree

  protected ArbolNodo nodoActual;
  protected ArbolNodo nodoSiguiente;

  ////////////////////////////////////////
  // Whether or not the current function 
  // is blocking (not executing) until 
  // another function completes its task.

  protected boolean bloqueado        = false;
  protected boolean ajusteArbolHecho = false;

  ////////////////////////////////////////
  // The current level in the tree

  protected int level = 0;

  /** The number of levels in the binary tree at the start of the
   *  insert operation. If the number of levels changes by the end
   *  of the operation then the ExtenderArbol function will be called
   *  to uncrowd the tree.
   */
  protected int iniciarNivel;

  protected String actualizarInfo = "";

  //metodo que inserta un nodo nuevo
  public Insertar(int value, LienzoBinario canvas) {
    this.canvas     = canvas;
    this.iniciarNivel = canvas.getLevel();

    nodoNuevo = new ArbolNodo(new Point((int) (canvas.size().width / 2),
				     (int) (canvas.getNodeSize() / 2)),
				     canvas.frame, value);
  }


  public Insertar(ArbolNodo newNode, LienzoBinario canvas) {
    this.canvas     = canvas;
    this.iniciarNivel = canvas.getLevel();

    this.nodoNuevo    = newNode;
  }


  public void update(Observable observed, Object arg) {
    if (observed instanceof Resaltar) {
      bloqueado = false;
    }
    else if (observed instanceof MoveNode) {
      bloqueado        = false;
      ajusteArbolHecho = true;
    }
  }

  public void performFuncion() {

    if (bloqueado) {

      setChanged();
      notifyObservers(actualizarInfo);

    }
    else if (ajusteArbolHecho) {

      setChanged();
      notifyObservers((Object) nodoNuevo);

      terminado = true;
    }
    else {

      ////////////////////////////////////////
      // The tree is empty so create the root
      // node.

      if (canvas.getRootNode() == null) {

	////////////////////////////////////////
	// The root node always has rank 1.

	nodoNuevo.setRank(1);
	
	canvas.addNode(nodoNuevo);

	Funcion mover = new MoveNode(
			   new Point((int) (canvas.size().width / 2),
				     (int) (canvas.getNodeSize() * 4)), 
			   nodoNuevo);
	mover.addObserver(this);
	
	canvas.addFunction(mover);

	////////////////////////////////////////
	// Notify observers that a new node has
	// been inserted.

	actualizarInfo = "Nodo raiz creado con valor: " + nodoNuevo.getValue();

	setChanged();
	notifyObservers(actualizarInfo);

	////////////////////////////////////////
	// Nothing else to do, so wait until 
	// tree adjustment completes.

	bloqueado = true;
      }

      ////////////////////////////////////////
      // Else if we have just started the in-
      // sertion function, retrieve the root
      // node and highlight it (indication of
      // comparison).

      else if (nodoActual == null) {
	nodoActual = canvas.getRootNode();

	Funcion highLight = new Resaltar(nodoActual, canvas);
	highLight.addObserver(this);

	canvas.addFunction(highLight);

	actualizarInfo = "Iniciando la busqueda desde la raiz: " + nodoActual.getValue();

	setChanged();
	notifyObservers(actualizarInfo);

	////////////////////////////////////////
	// Wait until the Resaltar function
	// terminates (Insertar will receive noti-
	// fication of this via functionFinished)

	bloqueado = true;
      }

      ////////////////////////////////////////
      // Else, do comparison on current node
      // and either insert new node as a child
      // or continue Insertion process on the
      // next node (either left or right child
      // of current node).

      else {

	if (nodoActual.menorQue(nodoNuevo)) {
	  
	  nodoSiguiente = nodoActual.getLeftChild();

	  ////////////////////////////////////////
	  // Insertar new node as a left child.

	  if (nodoSiguiente == null) {

	    ////////////////////////////////////////
	    // Set parent's left child pointer and
	    // new node's parent pointer.

	    nodoActual.setLeftChild(nodoNuevo);
	    nodoNuevo.setParent(nodoActual);

	    ////////////////////////////////////////
	    // Set rank to 2 * parent's rank and add
	    // the new node to the tree.

	    nodoNuevo.setRank(nodoActual.getRank() * 2);
	    canvas.addNode(nodoNuevo);

	    ////////////////////////////////////////
	    // Perform the left child insertion func-
	    // tion (graphics operation).

	    nodoNuevo.moveNode(nodoActual.origin().x,
			     nodoActual.origin().y);

	    int[] values = canvas.getLevelValues(level);
	    Funcion mover = new MoveNode(new Point(nodoNuevo.origin().x -
	      values[0], nodoNuevo.origin().y + values[1]), nodoNuevo);
	    mover.addObserver(this);

	    canvas.addFunction(mover);

	    actualizarInfo = "Nuevo nodo : " + nodoNuevo.getValue() + 
	      " Agregado a la izquierda : " + nodoActual.getValue();

	    setChanged();
	    notifyObservers(actualizarInfo);

	    if (iniciarNivel < canvas.getLevel())
	      canvas.addFunction(new ExtenderArbol(canvas));
	      
	    ////////////////////////////////////////
	    // Nothing else to do, so wait until
	    // tree adjustment is done.

	    bloqueado = true;
	  }

	  ////////////////////////////////////////
	  // Else continue process on the next
	  // node (left child or current node).

	  else {

	    nodoActual = nodoSiguiente;

	    Funcion highLight = new Resaltar(nodoActual, canvas);
	    highLight.addObserver(this);

	    canvas.addFunction(highLight);

	    actualizarInfo = "Node : " + nodoActual.getValue() + 
	      " Comparado con el valor : " + nodoNuevo.getValue();

	    setChanged();
	    notifyObservers(actualizarInfo);

	    ////////////////////////////////////////
	    // Wait until the Resaltar function
	    // terminates (Insertar will receive noti-
	    // fication of this via functionFinished)

	    bloqueado = true;

	    ////////////////////////////////////////
	    // Increment the level variable.

	    level++;
	  }
	}
	else if (nodoActual.mayorQue(nodoNuevo)) {

	  nodoSiguiente = nodoActual.getRightChild();

	  ////////////////////////////////////////
	  // Insertar new node as a right child.

	  if (nodoSiguiente == null) {

	    ////////////////////////////////////////
	    // Set parent's right child pointer and
	    // new node's parent pointer.

	    nodoActual.setRightChild(nodoNuevo);
	    nodoNuevo.setParent(nodoActual);

	    ////////////////////////////////////////
	    // Set rank to (2 * parent's rank) + 1
	    // and add new node to tree.

	    nodoNuevo.setRank((nodoActual.getRank() * 2) + 1);
	    canvas.addNode(nodoNuevo);

	    ////////////////////////////////////////
	    // Perform the left child insertion func-
	    // tion (graphics operation).
	    
	    nodoNuevo.move(nodoActual.origin().x,
			 nodoActual.origin().y);

	    int[] values = canvas.getLevelValues(level);
	    Funcion mover = new MoveNode(new Point(nodoNuevo.origin().x +
              values[0], nodoNuevo.origin().y + values[1]), nodoNuevo);
	    mover.addObserver(this);

	    canvas.addFunction(mover);

	    actualizarInfo = "Nuevo nodo : " + nodoNuevo.getValue() + 
	      " Agregado a la derecha : " + nodoActual.getValue();

	    setChanged();
	    notifyObservers(actualizarInfo);

	    if (iniciarNivel < canvas.getLevel())
	      canvas.addFunction(new ExtenderArbol(canvas));
	    
	    ////////////////////////////////////////
	    // Nothing else to do, so wait until
	    // tree adjustment is done.

	    bloqueado = true;
	  }

	  ////////////////////////////////////////
	  // Else continue process on the next
	  // node (right child or current node).

	  else {

	    nodoActual = nodoSiguiente;

	    Funcion highLight = new Resaltar(nodoActual, canvas);
	    highLight.addObserver(this);

	    canvas.addFunction(highLight);

	    actualizarInfo = "Nodo : " + nodoActual.getValue() +
	      " Valor comparado con : " + nodoNuevo.getValue();

	    setChanged();
	    notifyObservers(actualizarInfo);

	    ////////////////////////////////////////
	    // Wait until the Resaltar function
	    // terminates (Insertar will receive noti-
	    // fication of this via functionFinished)

	    bloqueado = true;

	    ////////////////////////////////////////
	    // Increment the level variable.

	    level++;
	  }
	}

	////////////////////////////////////////
	// We reach this point only if the new
	// node value already exists within the
	// tree. In this case we indicate term-
	// nation and do not modify the tree.

	else {

	  actualizarInfo = "Nodo : " + nodoNuevo.getValue() + 
	    " Ya existe en el arbol";

	  setChanged();
	  notifyObservers(actualizarInfo);

	  setChanged();
	  notifyObservers((Object) null);

	  terminado = true;
	}
      }
    }
  }
}
