package elaprendiz.estructuras.funciones;

import elaprendiz.graficos.funciones.Funcion;

import java.awt.Point;

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

import elaprendiz.graficos.imagenes.ImageFrame;
import elaprendiz.graficos.imagenes.ObjetoImagen;

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

public class Eliminar extends Funcion implements Observer {

  final static int CASO_UNO   = 0;
  final static int CASO_DOS   = 1;
  final static int CASO_TRES = 2;

  int casoPrueba;

  LienzoBinario lienzo = null;

  ArbolNodo borrarNodo = null;

  ArbolNodo hijoIzquierdo  = null;
  ArbolNodo hijoDerecho = null;
  ArbolNodo padre     = null;

  boolean busquedaTerminada = false;
  boolean casesDone  = false;
  boolean grupoNodos   = false;

  boolean bloqueado = false;

  int valor;

  int iniciarNivel;

  public Eliminar(int value, LienzoBinario canvas) {
    this.lienzo = canvas;
    this.valor  = value;

    iniciarNivel = canvas.getLevel();
  }

  public void update(Observable observed, Object arg) {
    if ( (observed instanceof Buscar) && (arg == null)) {
      busquedaTerminada = true;
      bloqueado    = false;
    }
    else if ( (observed instanceof Buscar) && (arg instanceof ArbolNodo)) {
      borrarNodo = (ArbolNodo) arg;

      busquedaTerminada = true;
      bloqueado    = false;
    }
    else if (observed instanceof MoverSubArbol) {
      casesDone = true;
      bloqueado   = false;
    }
  }

  public void performFuncion() {
    if (bloqueado) {
    }
    else if (busquedaTerminada == false) {
      
	Funcion search = new Buscar(valor, lienzo);
	search.addObserver(this);
	search.addObserver(lienzo);

	lienzo.addFunction(search);

	bloqueado = true;
    }
    else if ( (busquedaTerminada) && (borrarNodo == null)) {
      
      setChanged();
      notifyObservers((Object) ("Borrado fallido"));

      terminado = true;
    }
    else if (casesDone) {

      setChanged();
      notifyObservers((Object) ("Borrar Nodo : " + borrarNodo.getValue()));

      lienzo.clearDeleteNode();

      if (iniciarNivel > lienzo.getLevel())
	lienzo.addFunction(new ContratoArbol(lienzo));
      else
	lienzo.addFunction(new ArbolAjustado(lienzo));

      terminado = true;
    }
    else {

      if (grupoNodos == false) {
	hijoIzquierdo  = borrarNodo.getLeftChild();
	hijoDerecho = borrarNodo.getRightChild();
	padre     = borrarNodo.getParent();

	grupoNodos = true;
      }

      if ( (hijoIzquierdo == null) && (hijoDerecho == null)) {

	casoPrueba = CASO_UNO;

	lienzo.setDeleteNode(borrarNodo);
	lienzo.removeNode(borrarNodo);

	////////////////////////////////////////
	// Detach padre from node.

	if (padre != null) {
	  if (borrarNodo == padre.getLeftChild())
	    padre.setLeftChild(null);
	  else
	    padre.setRightChild(null);
	}

	////////////////////////////////////////
	// Detach child from padre.

	borrarNodo.setParent(null);

	Funcion mover = new MoverSubArbol(new Point(borrarNodo.origin().x,
	  lienzo.size().height +  lienzo.getNodeSize()), borrarNodo);
	mover.addObserver(this);

	lienzo.addFunction(mover);
	
	bloqueado = true;
      }
      else if (hijoIzquierdo == null) {

	casoPrueba = CASO_DOS;

	lienzo.setDeleteNode(borrarNodo);
	lienzo.removeNode(borrarNodo);

	////////////////////////////////////////
	// Detach padre from node and attach to
	// node's right child.

	if (padre != null) {
	  if (borrarNodo == padre.getLeftChild())

	    padre.setLeftChild(hijoDerecho);
	  else
	    padre.setRightChild(hijoDerecho);

	  hijoDerecho.setParent(padre);
	}
	else
	  hijoDerecho.setParent(null);

	////////////////////////////////////////
	// Detach node from padre and hijoDerecho
	// and attach right child to padre.

	borrarNodo.setParent(null);
	borrarNodo.setRightChild(null);

	hijoDerecho.setRank(borrarNodo.getRank());
	lienzo.addToRankList(hijoDerecho);

	Funcion moverOne = new MoverSubArbol(new Point(borrarNodo.origin().x,
	  lienzo.size().height + lienzo.getNodeSize()), borrarNodo);
	Funcion moverTwo = new MoverSubArbol(new Point(borrarNodo.origin().x,
	  borrarNodo.origin().y), hijoDerecho);
	moverOne.addObserver(this);
	moverTwo.addObserver(this);

	lienzo.addFunction(moverOne);
	lienzo.addFunction(moverTwo);

	bloqueado = true;
      }
      else if (hijoDerecho == null) {

	casoPrueba = CASO_DOS;

	lienzo.setDeleteNode(borrarNodo);
	lienzo.removeNode(borrarNodo);

	////////////////////////////////////////
	// Detach padre from node and attach to
	// node's right child.

	if (padre != null) {
	  if (borrarNodo == padre.getLeftChild()) 
	    padre.setLeftChild(hijoIzquierdo);
	  else
	    padre.setRightChild(hijoIzquierdo);

	  hijoIzquierdo.setParent(padre);
	}
	else
	  hijoIzquierdo.setParent(null);

	////////////////////////////////////////
	// Detach node from padre and hijoDerecho

	borrarNodo.setParent(null);
	borrarNodo.setLeftChild(null);

	hijoIzquierdo.setRank(borrarNodo.getRank());
	lienzo.addToRankList(hijoIzquierdo);

	Funcion moverOne = new MoverSubArbol(new Point(borrarNodo.origin().x,
	  lienzo.size().height + lienzo.getNodeSize()), borrarNodo);
	Funcion moverTwo = new MoverSubArbol(new Point(borrarNodo.origin().x,
	  borrarNodo.origin().y), hijoIzquierdo);
	moverOne.addObserver(this);
	moverTwo.addObserver(this);

	lienzo.addFunction(moverOne);
	lienzo.addFunction(moverTwo);

	bloqueado = true;
      }
      else {

	casoPrueba = CASO_TRES;

	lienzo.setDeleteNode(borrarNodo);
	lienzo.removeNode(borrarNodo);

	ArbolNodo successor = hijoDerecho.getLeastChild();
	if (successor != hijoDerecho)
	  successor.getParent().setLeftChild(null);

	ArbolNodo succChild = successor.getRightChild();
	
	if (succChild != null) {
	  succChild.setParent(successor.getParent());
	  successor.getParent().setLeftChild(succChild);

	  Funcion moverOne = new MoverSubArbol(new Point(successor.origin().x,
	    successor.origin().y), succChild);
	  moverOne.addObserver(this);

	  lienzo.addFunction(moverOne);
	}

	////////////////////////////////////////
	// Detach padre from node and attach to
	// node's right child.

	if (padre != null) {
	  if (borrarNodo == padre.getLeftChild()) 
	    padre.setLeftChild(successor);
	  else
	    padre.setRightChild(successor);

	  successor.setParent(padre);
	}
	else
	  successor.setParent(null);

	hijoIzquierdo.setParent(successor);
	successor.setLeftChild(hijoIzquierdo);

	if (hijoDerecho != successor) {
	  hijoDerecho.setParent(successor);
	  successor.setRightChild(hijoDerecho);
	}

	borrarNodo.setParent(null);
	borrarNodo.setLeftChild(null);
	borrarNodo.setRightChild(null);

	successor.setRank(borrarNodo.getRank());
	lienzo.addToRankList(successor);

	Funcion moverTwo   = new MoverSubArbol(new Point(borrarNodo.origin().x,
	  lienzo.size().height + lienzo.getNodeSize()), borrarNodo);
	Funcion moverThree = new MoveNode(new Point(borrarNodo.origin().x,
	  borrarNodo.origin().y), successor);
	moverTwo.addObserver(this);
	moverThree.addObserver(this);

	lienzo.addFunction(moverTwo);
	lienzo.addFunction(moverThree);

	bloqueado = true;
      }
    }
  }
}
