package IDAStarSearch;

import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.*;
import java.awt.event.*;

public class IDAStarSearch extends JApplet implements Runnable{

   static final int WIDTH = 800;
   static final int HEIGHT = 600;
   static final int CELL_SIZE = 15;

   BufferedImage bi = new BufferedImage( WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
   BufferedImage bb = new BufferedImage( WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);

   BufferedImage bgImage;
   BufferedImage workImage;

   StateNode rootNode = null;
   boolean running;

   boolean paused = false;

   private static Color[] squareColors = {
			      new Color( 31, 0,255 ),
			      new Color( 63, 0,255 ),
			      new Color( 96, 0,255 ),
			      new Color(128, 0,255 ),
			      new Color(159, 0,255 ),
			      new Color(191, 0,255 ),
			      new Color(223, 0,255 ),
			      new Color(255, 0,255 )
			   };

   public void start(){
      addMouseListener( new MouseAdapter(){
	 public void mouseClicked( MouseEvent e){
	    paused = !paused;
	 }
      });

      workImage = bb;
      bgImage = bi;
      Thread t = new Thread( this );
      running = true;
      t.start();
   }

   public void stop(){
      paused = false;
      running = false;
      rootNode = null;
      bgImage = null;
      workImage = null;
   }

   public void run(){
      while( running ){
	 System.out.println("running");
	 rootNode = StateNode.getStartNode();
	 IDAStar( rootNode );
	 try{
	    Thread.sleep(1000);
	 }catch(Exception e){
	    return;
	 }
      }
   }

   public void destroy(){
   }

   public void init(){
      //start();
   }


   public void paint(Graphics g){
      synchronized( bgImage ){
	 g.drawImage( bgImage, 0 , 0, WIDTH, HEIGHT, this);
      }
   }

   private void updateGraphics( StateNode focusNode, StateNode cNode, Graphics2D g ){
      // Draw "this" box 
      drawNode( cNode, g, CELL_SIZE , focusNode == cNode );

      // Move down one level:
      g.translate( 0, CELL_SIZE );

      Vector v = cNode.getKids();

      if( v != null ){

	 Iterator i = v.iterator();

	 while(i.hasNext()){
	    StateNode s = (StateNode) i.next();
	    updateGraphics( focusNode, s, g );
	 }

      }else{
	 g.translate( CELL_SIZE , 0 );
      }
      // Move back up
      g.translate( 0, -CELL_SIZE );
   }




   private void drawNode( StateNode cNode, Graphics g, int mult, boolean special ){

      for(int i = 0; i < 9 ; i++){
	 if( cNode._squares[i] != -1 ){
	    g.setColor( squareColors[cNode._squares[i]] );
	    g.fillRect( (mult / 3 ) * cNode.getXcoord(i), (mult / 3 ) * cNode.getYcoord(i), mult / 3, mult / 3 );
	 }
      }

      if(special){
	 g.setColor( Color.RED );
      }else{
	 g.setColor( Color.GREEN );
      }

	 g.drawRect( 0, 0, (int) mult, (int)mult);
      
   }

   private int offSet;
   private boolean offSetFound;

   private void pulseUpdate( StateNode focusNode ){
      while( paused ){
	 Thread.yield();
      }
      offSet = 0;
      offSetFound = false;
      long totalWidth = calculateWidth( rootNode, focusNode );
      Graphics2D g2 = (Graphics2D) workImage.getGraphics();
      g2.clearRect(0,0,WIDTH,HEIGHT);
      g2.setColor( Color.GREEN );


      /*
      double mult = ((double) WIDTH) / ((double)totalWidth);
      */

      /* Now offset this window so current node is in the middle */

      

      g2.translate( (((WIDTH / 2) - (CELL_SIZE * offSet))) , ((HEIGHT/2) - (getHeight( focusNode) * CELL_SIZE) ));


      
      

      updateGraphics( focusNode, rootNode, g2);

      g2.translate( -totalWidth * CELL_SIZE , 0);
      switchImage();
      repaint();



      try{
	 Thread.sleep( 10 );
	 Thread.yield();
      }catch(Exception e){
      }

   }


   private long calculateWidth( StateNode s, StateNode focusNode ){
      /* Quick hack solution */
      if( s == null ){ return 1; }
      if( s == focusNode ){ offSetFound = true; }

      long tmp = 0;
      Vector kids = s.getKids();

      if( kids == null ){
	 if( !offSetFound ){
	    offSet++;
	 }
	 s.setWidth( 1 );
	 return 1;
      }else{
	 Iterator i = kids.iterator();
	 while( i.hasNext() ){
	    StateNode sK = (StateNode) i.next();
	    tmp += calculateWidth( sK, focusNode );
	 }
	 s.setWidth( tmp );
	 return tmp;
      }

   }

   private int getHeight( StateNode n ){
      return 10;
      /*
      if( n == null ){ return 0; }
      return 1 + getHeight(n.getParent());
      */
   }

   private synchronized void switchImage(){
      synchronized( bgImage ){
	 BufferedImage tmp;
	 tmp = bgImage;
	 bgImage = workImage;
	 workImage = tmp;
      }
   }





   private static final int INFINITY = Integer.MAX_VALUE;
   /* Well its not quite infinity, but it should be good enough */


   private StateNode IDAStar( StateNode sn ){
      /* sn is the start node in the problem */
      int fLimit; /* current f-Cost Limit */

      fLimit = fCost( sn );

      while(true){
	 SolutionLimitPair slp = DFSContour( sn, fLimit );
	 fLimit = slp.getNewF();
	 if( slp.getSolution() != null ){ 
	    return slp.getSolution(); 
	 }

	 if( fLimit == INFINITY ){ 
	    return null;
	 }
      }
   }


   private SolutionLimitPair DFSContour( StateNode sn, int fLimit ){
      /* flimit is current f-Cost limit */
      int nextF = INFINITY;
      /* nextF is the fCost Limit for the next contour, initially INFINITY */

pulseUpdate( sn );
      if( fCost( sn ) > fLimit ){ 
	 return new SolutionLimitPair( null, fCost( sn ) ) ;
      };

      if( goalTest( sn ) ){ 
	 return new SolutionLimitPair( sn, fLimit ); 
      }

      Vector successors = sn.getSuccessors();
      Iterator i = successors.iterator();

      while( i.hasNext() ){
	 StateNode s = (StateNode) i.next();


	 SolutionLimitPair slp = DFSContour( s, fLimit );
	 if( slp.getSolution() != null ){
	    return slp;
	 }
	 nextF = min( nextF, slp.getNewF() );
      }

      return new SolutionLimitPair( null, nextF );
   }

   private int fCost( StateNode sn ){
      return sn.getCost();
   }

   private boolean goalTest( StateNode sn ){
      return sn.isGoal();
   }

   private int min( int a, int b ){
      return ((a < b) ? a : b);
   }

}

