/*
 * Burton Rosenberg
 * June 2004
 */

import java.awt.* ;
import javax.swing.* ;

class TreeViewer 
extends JPanel {

   final static int OFFSET_Y = 10 ;
   final static int NODE_OFFSET = 2 ;
   final static int NODE_WIDTH = 2*NODE_OFFSET+1 ;

   // perhaps should search for closest pick
   final static int PICK_BOX = 10 ;

   final static Color LINE_COLOR = Color.blue ;
   final static Color NODE_COLOR = Color.red ;
   final static Color LEAF_COLOR = Color.green ;
   final static Color TEXT_COLOR = Color.black ;

   TreeModel tm ;

   final static int VIEW_STYLE_TEXT = 0 ;
   final static int VIEW_STYLE_GEOMETRIC = 1 ;
   final static int VIEW_STYLE_EQUAL_SPACED = 2 ;

   int xPick ;
   int yPick ;
   boolean isPickedEnable = false ;

   int viewStyle = VIEW_STYLE_GEOMETRIC ;

   // constants for VIEW_STYLE_TEXT
   final static int X_ORIG = 10 ;
   final static int Y_ORIG = 10 ;
   final static int X_SKIP = 20 ;
   final static int Y_SKIP = 20 ;
   final static int X_OFFSET = 3 ;
   final static int Y_OFFSET = 3 ;

   TreeViewer( TreeModel tm ) {
      this.tm = tm ;
   }

   void mousePick( int x, int y, boolean b ) {
      xPick = x ;
      yPick = y ;
      isPickedEnable = b ;
   }

   public void paintComponent(Graphics gc) {
       super.paintComponent(gc) ;
       Point p ;
       Tree.Node tn ;
       Dimension d = getSize() ;

       switch ( viewStyle ) {

       case VIEW_STYLE_TEXT:
          drawTreeZero( gc, tm.getTree().getRoot(), X_ORIG, Y_ORIG ) ;
          break ;

       case VIEW_STYLE_GEOMETRIC:
          tn = tm.getTree().getRoot() ;
          if ( tn==null ) return ;
          p = drawTreeOne( gc, tn, 0, d.width, OFFSET_Y ) ;
          if ( p!=null ) {
            gc.setColor( NODE_COLOR ) ;
            gc.fillRect(p.x-NODE_OFFSET,p.y-NODE_OFFSET,NODE_WIDTH,NODE_WIDTH) ;
            if ( isPicked( p.x, p.y ) ) {
               gc.setColor( TEXT_COLOR ) ;
               tm.pickedString = tn.key.key_s ;
               gc.drawString( tn.key.key_s, p.x, p.y ) ;
            }
          }
          break ;

       case VIEW_STYLE_EQUAL_SPACED:
          tm.getTree().computeCount() ;
          tn = tm.getTree().getRoot() ;
          if ( tn==null ) return ;
          int xs = d.width / (tn.count+1) ;
          p = drawTreeTwo( gc, tn, xs, 0, OFFSET_Y ) ;
          if ( p!=null ) {
            gc.setColor( NODE_COLOR ) ;
            gc.fillRect(p.x-NODE_OFFSET,p.y-NODE_OFFSET,NODE_WIDTH,NODE_WIDTH) ;
            if ( isPicked( p.x, p.y ) ) {
               gc.setColor( TEXT_COLOR ) ;
               tm.pickedString = tn.key.key_s ;
               gc.drawString( tn.key.key_s, p.x, p.y ) ;
            }
          } 
          break ;
       }
   }

   Point drawTreeTwo( Graphics gc, Tree.Node tni, 
       int xSpacing, int xLeftBorder, int y ) {
       // returns where it placed tni; caller must paint it.

       Point p ;
       if ( tni==null ) return null ;
       int x = xLeftBorder + xSpacing * ((tni.lc==null)? 1 : (tni.lc.count+1)) ;

       if ( tni.lc!=null ) {
          p = drawTreeTwo( gc, tni.lc, xSpacing, xLeftBorder, y+OFFSET_Y ) ;
          gc.setColor( LINE_COLOR ) ;
          gc.drawLine( p.x, p.y, x, y ) ;
          gc.setColor( NODE_COLOR ) ;
          gc.fillRect(p.x-NODE_OFFSET,p.y-NODE_OFFSET,NODE_WIDTH,NODE_WIDTH) ;
          if ( isPicked( p.x, p.y ) ) {
              gc.setColor( TEXT_COLOR ) ;
              tm.pickedString = tni.lc.key.key_s ;
              gc.drawString( tni.lc.key.key_s, p.x, p.y ) ;
          }
       }
       if ( tni.rc!=null ) {
          p = drawTreeTwo( gc, tni.rc, xSpacing, x, y+OFFSET_Y ) ;
          gc.setColor( LINE_COLOR ) ;
          gc.drawLine( p.x, p.y, x, y ) ;
          gc.setColor( NODE_COLOR ) ;
          gc.fillRect(p.x-NODE_OFFSET,p.y-NODE_OFFSET,NODE_WIDTH,NODE_WIDTH) ;
          if ( isPicked( p.x, p.y ) ) {
              gc.setColor( TEXT_COLOR ) ;
              tm.pickedString = tni.rc.key.key_s ;
              gc.drawString( tni.rc.key.key_s, p.x, p.y ) ;
          }
       }
       return new Point(x,y) ;
   }

   Point drawTreeOne( Graphics gc, Tree.Node tni, 
       int xMin, int xMax, int y ) {

       Point p ;
       if ( tni==null ) return null ;
       int x = (xMin+xMax)/2 ;
       if ( tni.lc!=null ) {
          p = drawTreeOne( gc, tni.lc, xMin, x, y+OFFSET_Y) ;
          gc.setColor( LINE_COLOR ) ;
          gc.drawLine( p.x, p.y, x, y ) ;
          gc.setColor( NODE_COLOR ) ;
          gc.fillRect(p.x-NODE_OFFSET,p.y-NODE_OFFSET,NODE_WIDTH,NODE_WIDTH) ;
          if ( isPicked( p.x, p.y ) ) {
              gc.setColor( TEXT_COLOR ) ;
              tm.pickedString = tni.lc.key.key_s ;
              gc.drawString( tni.lc.key.key_s, p.x, p.y ) ;
          }
       }
       if ( tni.rc!=null ) {
          p = drawTreeOne( gc, tni.rc, x, xMax, y+OFFSET_Y) ;
          gc.setColor( LINE_COLOR ) ;
          gc.drawLine( p.x, p.y, x, y ) ;
          gc.setColor( NODE_COLOR ) ;
          gc.fillRect(p.x-NODE_OFFSET,p.y-NODE_OFFSET,NODE_WIDTH,NODE_WIDTH) ;
          if ( isPicked( p.x, p.y ) ) {
              gc.setColor( TEXT_COLOR ) ;
              tm.pickedString = tni.rc.key.key_s ;
              gc.drawString( tni.rc.key.key_s, p.x, p.y ) ;
          }
       }
       return new Point(x,y) ;
   }

   void drawTreeZero( Graphics gc, Tree.Node tni,
                      int x, int y ) 
   {
      if ( tni == null )
      {
         gc.setColor( LEAF_COLOR ) ;
         gc.drawString( "The tree is empty!", x+2*X_OFFSET, y+Y_OFFSET ) ;
      }
      else
      {    
         // tree is not null
         gc.setColor( LINE_COLOR ) ;
         gc.drawLine( x-X_OFFSET, y, x+X_OFFSET, y ) ;
         gc.setColor( TEXT_COLOR ) ;
         gc.drawString( tni.key.key_s, x+2*X_OFFSET, y+Y_OFFSET ) ;
         y += Y_SKIP ;
         y = drawTreeZeroAux( gc, tni.lc, x, y, x+X_SKIP, y ) ;
         y = drawTreeZeroAux( gc, tni.rc, x, Y_ORIG, x+X_SKIP, y ) ;
       }    
   }

   int drawTreeZeroAux( Graphics gc, Tree.Node tni, 
             int xp, int yp, int x, int y ) 
   {
     // draw line from (xp,yp) -> (x,y)
      gc.setColor( LINE_COLOR ) ;
      gc.drawLine( xp, yp, xp, y ) ;
      gc.drawLine( xp, y, x+X_OFFSET, y ) ;

      if ( tni == null )
      {
         gc.setColor( LEAF_COLOR ) ;
         gc.drawString( "-null-", x+2*X_OFFSET, y+Y_OFFSET ) ;
         y += Y_SKIP ;
      }
      else
      {
         gc.setColor( TEXT_COLOR ) ;
         gc.drawString( tni.key.key_s, x+2*X_OFFSET, y+Y_OFFSET ) ;
 
         // remember current location, for recursive calls
         xp = x ;
         yp = y ;

         y += Y_SKIP ;
         y = drawTreeZeroAux( gc, tni.lc, xp, yp, x+X_SKIP, y ) ;
         y = drawTreeZeroAux( gc, tni.rc, xp, yp, x+X_SKIP, y ) ;

      }
      return y ;

   }

   boolean isPicked(int x, int y) {
      if ( isPickedEnable ) {
         if (xPick-x<PICK_BOX && x-xPick<PICK_BOX 
             && yPick-y<PICK_BOX && y-yPick<PICK_BOX )
         return true ;
      }
      return false ;
   }



}

