/*	This Applet was created by Michael Morton for use by the Math Forum.
You may use pieces of code only with the consent of the Math Forum. 
*/

/* This is the Canvas for the Traffic Jam Applet. It draws the images, 
handles the movements thereof, and the counter/goal_strings.
*/

import java.awt.*;

public class JamCan extends Panel {
  
  // Setup Control Ints	
  final int NUM_ROWS = 1, PIC_WIDTH = 34, PIC_HEIGHT = 52, 
    NUM_TOTAL = 9, NUM_COLS = 9, GAP = 0, STR_GAP = 0,
    X_INSET = 50, Y_INSET = 10, XTOP=125, YTOP=35;
  
  // lowest = lowest possible moves for this level
  int curr_cols = 7, temp, jam_level = 2, lowest=15;
  Image all_pics[] = new Image[NUM_TOTAL];
  // pics_order keeps track of the current order of the pics
  int pics_order[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
  // goal_order keeps track of the goal order for the current level
  int goal_order[] = {0, 5, 6, 7, 4, 1, 2, 3, 8};
  int xpos, ypos, blank=4, counter=0, counter_x, counter_y, change=0;
  // box[][] keeps track of where each image can be moved (drawn)
  int box[][] = new int[NUM_TOTAL][4];	
  Color fgjamcolor=Color.white,bgjamcolor=Color.black;
  Font all_font,top_font;
  Jam outerparent;
  
  JamCan(Jam target, Image all_da_pics[]) {
    
    // Copy over the pics so globally availably here
    for(int i = 0; i < NUM_TOTAL; i++)
      all_pics[i] = all_da_pics[i];
    
    // So can contact parent
    this.outerparent = target;
    
    // Set Font
    all_font = new Font("TimesRoman", Font.PLAIN, 20);
    top_font = new Font("TimesRoman", Font.BOLD, 30);
    
    // Set Background
    setBackground(bgjamcolor);
    
    //Setup where the counter: # will be printed on the Frame
    counter_x = X_INSET;
    counter_y = (NUM_ROWS * (PIC_HEIGHT+GAP)) + Y_INSET+YTOP + 30;
    
    // This box[][] contains info about where each 'person' is printed
    // on the screen. The first [] are the 9 places where could possibly
    // be printing a 'person'.  The second [] contains 4 ints.  They are:
    // top-left corner x and y position, x-width and y-height. This
    // information is mostly used when you click to find out which 
    // 'person' you clicked on.
    for (int i = 0; i < NUM_TOTAL; i++) {
      box[i][0] = (int)(X_INSET + (GAP + PIC_WIDTH) *
			(i % NUM_COLS));     // X-POS
      box[i][1] = (int)(Y_INSET + YTOP + (GAP + PIC_HEIGHT) *
			Math.floor(i / NUM_COLS));   // Y-POS
      box[i][2] = box[i][0] + PIC_WIDTH;       // X-WIDTH
      box[i][3] = box[i][1] + PIC_HEIGHT;      // Y-HEIGHT
    }
    
  }
  
  // This handles the clicking. It takes as arguments where you click.
  // It first runs through the shown 'people' (using jam_level); using
  // the boxes[][] it can quickly check if you did indeed click on a 'person'
  public boolean mouseDown(Event evt, int x, int y) {
    for (int i = 3-jam_level; i <= 5+jam_level; i++) {
      if (x > box[i][0] && x < box[i][2] &&
	  y > box[i][1] && y < box[i][3]) {
	// Depending on which 'person' is clicked on, it checks to see if it's
	// a legal move. If it is, it switches the two 'people, adds to 
	// the counter, and re-draws.
	if (is_legal(i)){
	  temp = pics_order[i];
	  pics_order[i] = pics_order[blank];
	  pics_order[blank] = temp;
	  outerparent.history.change(i,blank);
	  blank = i;
	  counter++;
	  repaint();
	}
      }
    }
    return true;				
  }
  
  // Over-ride main update so that it only re-draws the 'people' not
  // background (no flicker then). But, if there is a change in the level
  // (i.e. reset everything), need to re-draw the background. thus, the 
  // change variable.
  public void update(Graphics g) {
    if(change == 1) {
      setBackground(bgjamcolor);
      g.setColor(bgjamcolor);
      g.fillRect(0,0,size().width,size().height);
      change = 0;
    }
    paint(g);
  }
  
  public void paint (Graphics g) {
    String counter_string = "Counter: " + counter;
    String finish_string = "You got it!";
    String top_string = "Traffic Jam!";
    String best_string = "Completed in the Least Number of Moves!";
    String tryagain_string = 
      "Good job. Try again to complete in less moves.";
    FontMetrics fm = getFontMetrics(all_font);
    String this_string;
    int str_y;
    
    // Draw the topString
    g.setColor(fgjamcolor);
    g.setFont(top_font);
    g.drawString(top_string,XTOP,YTOP);			
    
    // Draw the pics in the correct order. (and draw the correct
    // number of them (for the level).
    for (int j = 3-jam_level; j <= 5+jam_level; j++) {
      if(pics_order[j] == 4) {
	g.setColor(bgjamcolor);
	g.fillRect(box[j][0],box[j][1], 
		   PIC_WIDTH, PIC_HEIGHT);
      }
      else
	g.drawImage(all_pics[pics_order[j]], 
		    box[j][0], box[j][1], this);
    }
    
    
    // Draw Counter. Draw a rectangle in background color 
    // over old number, put up the new number.
    
    g.setFont(all_font);
    g.setColor(bgjamcolor);
    g.fillRect(counter_x + fm.stringWidth("Counter: "), 
	       counter_y - fm.getAscent(), 
	       fm.stringWidth("9999"), fm.getHeight());
    g.setColor(fgjamcolor);
    g.drawString(counter_string, counter_x, counter_y);
    
    // Draw Finish. Draw a rectangle in the background color.
    str_y = counter_y + fm.getHeight() + STR_GAP;	
    g.setColor(bgjamcolor);
    g.fillRect(counter_x, str_y - fm.getAscent(),
	       fm.stringWidth(finish_string), fm.getHeight());
    g.setColor(fgjamcolor);
    if (goal_state())   // If goal is true, draw the Win String.
      g.drawString(finish_string, counter_x, str_y);
    
    // Draw Win/Try again.
    if (counter == lowest) this_string = best_string;
    else this_string = tryagain_string;
    
    str_y = str_y + fm.getHeight() + STR_GAP;
    g.setColor(bgjamcolor);
    g.fillRect(counter_x, str_y - fm.getAscent(), 
	       fm.stringWidth(this_string), fm.getHeight());
    g.setColor(fgjamcolor);
    if (goal_state()) 
      g.drawString(this_string, counter_x, str_y);
    
  }
  
  // This function is called when click on a level to reset and start
  // at that level.
  void update_level(int level) {
    
    for(int i = 0;i < NUM_TOTAL;i++) 
      pics_order[i] = i;         // Reset the pics order 
    // to starting order.
    
    // These set up the goal order. this is funky b/c if the level
    // is not the hardest, some pics never get moved (at the ends)
    // and so, kinda funky order of numbers.
    if(level == 1) {
      goal_order[0] = 0;goal_order[1] = 1;goal_order[2] = 5;
      goal_order[3] = 6;goal_order[4] = 4;goal_order[5] = 2;
      goal_order[6] = 3;goal_order[7] = 7;goal_order[8] = 8;
      lowest = 8;
    }
    else if(level == 2) {
      goal_order[0] = 0;goal_order[1] = 5;goal_order[2] = 6;
      goal_order[3] = 7;goal_order[4] = 4;goal_order[5] = 1;
      goal_order[6] = 2;goal_order[7] = 3;goal_order[8] = 8;
      lowest = 15;
    }
    else if(level == 3) {
      goal_order[0] = 5;goal_order[1] = 6;goal_order[2] = 7;
      goal_order[3] = 8;goal_order[4] = 4;goal_order[5] = 0;
      goal_order[6] = 1;goal_order[7] = 2;goal_order[8] = 3;
      lowest = 24;
    }
    
    // Reset the variables.	
    jam_level = level;
    change = 1;          // Set to 1 so that it re-draws the Frame
    curr_cols = 3+2*level;
    counter = 0;
    blank = 4;
    
    repaint();
    
  }
  
  // This changes the Foreground or Background Color variable, and
  // re-draws the Frame
  void updatecolor(String choice, Color col) {
    if(choice.equals("fg"))
      fgjamcolor = col;
    else if(choice.equals("bg")) {
      bgjamcolor = col;
      change = 1;
    }						
    repaint();
  }
  
  // This checks if the pics_order is the same as the goal_order.
  // If it is, it return True.
  public boolean goal_state() {
    boolean ret;
    
    for (int i = 0; i < curr_cols; i++) {
      if (pics_order[i] == goal_order[i])
	;
      else 
	return false;
    }
    return true;
  }
  
  // This checks to see if it's a legal move. Return True if it is.
  public boolean is_legal (int i) {
    boolean ret=false;
    
    if (blank == (i+1) || blank == (i-1))     // If blank is on 
      ret = true;		 	  // either side: True.
    
    // If the blank is one space away (+-2) and the one b/w is an
    // opposite 'person': True.
    else if ((blank == (i+2) && is_opposite(i,i+1)) ||
	     (blank == (i-2) && is_opposite(i,i-1)))
      ret = true;
    return ret;
  }
  
  // Checks to see if the two 'people' are opposites.
  public boolean is_opposite(int i, int j) {
    boolean ret = false;
    
    if(Math.abs(pics_order[i] - pics_order[j]) > 1)
      ret = true;
    return ret;
  }
  
}

