/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
*                                                                     *
* \                                                                   *
*  |- MagCan.java                                                     *
*  |-  This class defines the main panel of the applet, in            *
*  |-  which the magic square and all the blocks are displayed.       *
* /                                                                   *
* \                                                                   *
*  |- Author: Mike Morton                                             *
*  |- Created: Summer 1996                                            *
* /                                                                   *
* \                                                                   *
*  |- Revised on: 12/02/03                                            *
*  |- by Pavel Safronov                                               *
* /                                                                   *
* \                                                                   *
*  |- Last updated: 1/22/04                                           *
*  |- by Michael McKelvey                                             *
* /                                                                   *
*                                                                     *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

import java.awt.Panel;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Point;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.*;


public class MagCan extends Panel {

  // Setup Control Ints
  int NUM_ROWS = 3,  NUM_COLS = 3, BOX_WIDTH = 25,
    BOX_HEIGHT = 25, BOX_GAP = 5, STR_X_GAP = 15,
    STR_Y_GAP = 0, PUZ_GAP = 30, X_INSET = 50, Y_INSET = 25,
    BSTR_X = 8, BSTR_Y = 20, B2STR_X = 3;

  int NUM_TOTAL = NUM_ROWS * NUM_COLS;
  int MARG_ERR = (int)Math.floor(.35 * BOX_WIDTH);
  int X_SIZE = getSize().width;
  int Y_SIZE = getSize().height;
  int rows[] = new int[7];
  int cols[] = new int[7];
  int diags[] = new int[2];

  boolean goal = false;
//  Color goal_color = new Color(1000,20000,60535);
//  Color goal_color = new Color(255,0,0);
  Color goal_color = new Color(232,32,119);
  Font goal_font = new Font("TimesRoman", Font.BOLD, 50);

  Font box_font = new Font("TimesRoman", Font.BOLD, 18);
  Font str_font = new Font("TimesRoman", Font.PLAIN, 14);
  Font top_font = new Font("TimesRoman", Font.BOLD, 36);
  FontMetrics fm = getFontMetrics(box_font);
  FontMetrics fm2 = getFontMetrics(str_font);
  String longest_string = "Row 9: 999";
  String row_str[] = {"Row 1: ","Row 2: ","Row 3: ","Row 4: ","Row 5: ","Row 6: ","Row 7: "};

  int temp, str_x, str_y, x_diff, y_diff;
  int grid_locs[][] = new int[49][3];
  int boxes[][] = new int[49][3];
  int currbox, row, xdiff, ydiff, inbox = 0;
  Color fgmagcan=Color.black,bgmagcan=Color.white, numbers=Color.black,
    boxcolor=Color.yellow, selectedboxcolor=new Color(255,153,153), outline=Color.red;
  MagPuz outerparent;
  Point curr;

  MagCan(MagPuz target) {
    // Added by MLM 1/15/03
    this.setSize(new Dimension(550,200));
    row = 5;

    // So can contact parent
    this.outerparent = target;

    // Set Background
    setBackground(bgmagcan);

    // This boxes[] contains info about where each box is printed
    // on the screen. Each one contains an x-pos, y-pos, and
    // the number in the box. This routine sets initial positions.

    update_level(3);

    // Setup the first current box
    currbox = 0;
  }

  // This handles the clicking. It takes as arguments where you click.
  // It first runs through the boxes to find out which box you clicked in.
  // It returns this box as the current box.
  public boolean mouseDown(Event evt, int x, int y) {
    for (int i = 0; i <= NUM_TOTAL; i++) {
      if (x > boxes[i][0] && x < boxes[i][0]+BOX_WIDTH &&
	  y > boxes[i][1] && y < boxes[i][1]+BOX_HEIGHT) {
	currbox = i;
	xdiff = (x-boxes[currbox][0]);
	ydiff = (y-boxes[currbox][1]);
	curr = new Point(x,y);
	inbox = 1;
	break;
      }
      else
	inbox = 0;
    }
    return true;
  }

  public boolean mouseDrag(Event evt, int x, int y) {

    if(inbox == 1) {
      curr = new Point(x,y);
      repaint();
    }
    return true;
  }

  public boolean mouseUp(Event evt, int x, int y) {

    if(inbox == 1){
      boxes[currbox][0]=(curr.x-xdiff);
      boxes[currbox][1]=(curr.y-ydiff);
      curr = null;
      // Check if a box has been moved into/out of a grid_loc
      in_grid_box();
      // Add up current grid_locs
      addup();
      // Check it its a goal_state
      goal_state();
      repaint();
    }
    return true;
  }

  void in_grid_box() {

    for(int i=0;i<NUM_TOTAL;i++){

      if((boxes[currbox][0] < grid_locs[i][0]+MARG_ERR &&
	  boxes[currbox][0] > grid_locs[i][0]-MARG_ERR) &&
	 (boxes[currbox][1] < grid_locs[i][1]+MARG_ERR &&
	  boxes[currbox][1] > grid_locs[i][1]-MARG_ERR)) {
	boxes[currbox][0] = grid_locs[i][0];
	boxes[currbox][1] = grid_locs[i][1];
	grid_locs[i][2] = currbox;
      }
      else
	if(grid_locs[i][2] == currbox)
	  grid_locs[i][2] = -1;
    }
  }


  // Over-ride main update so that it only re-draws the boxes, not
  // the rest of the screen. Needs work.
  /*
    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);
    }
*/



///////// Added by MLM (1/15/04) /////////
///////// Double-Buffering  //////////////
	public void update(Graphics g)
	{
		Graphics offgc;
		Image offscreen = null;
		Dimension d = size();

		// create the offscreen buffer and associated Graphics
		offscreen = createImage(d.width, d.height);
		offgc = offscreen.getGraphics();
		// clear the exposed area
		offgc.setColor(getBackground());
		offgc.fillRect(0, 0, d.width, d.height);
		offgc.setColor(getForeground());
		// do normal redraw
		paint(offgc);
		// transfer offscreen to window
		g.drawImage(offscreen, 0, 0, this);
    }
///////// Added by MLM (1/15/04) /////////



  public void paint(Graphics g) {

    // Draw the Grid
    g.setColor(fgmagcan);
    for(int i=1;i<NUM_COLS;i++) {
      g.fillRect(grid_locs[i][0]-BOX_GAP,
		 grid_locs[i][1], BOX_GAP,
		 (BOX_HEIGHT+BOX_GAP)*NUM_ROWS-BOX_GAP);
      g.fillRect(grid_locs[i*NUM_COLS][0],
		 grid_locs[i*NUM_COLS][1]-BOX_GAP,
		 (BOX_WIDTH+BOX_GAP)*NUM_COLS-BOX_GAP,BOX_GAP);
    }

    // Draw the addition
    g.setFont(str_font);
    g.setColor(fgmagcan);
    g.drawString("Cols:",5,NUM_ROWS*(BOX_HEIGHT+BOX_GAP)+
		 str_y+fm2.getHeight());
    for(int i=0;i<NUM_ROWS;i++){
      g.drawString(row_str[i]+String.valueOf(rows[i]),str_x,
		   str_y+i*(BOX_HEIGHT+BOX_GAP)+fm2.getHeight());
      g.drawString(String.valueOf(cols[i]),grid_locs[i][0]+5,
		   NUM_ROWS*(BOX_HEIGHT+BOX_GAP)+str_y+
		   fm2.getHeight());
    }
    for(int i=0;i<2;i++)
      g.drawString("Diagonal: "+String.valueOf(diags[i]),
		   str_x, fm2.getHeight()+
		   i*(20+(BOX_HEIGHT+BOX_GAP)*NUM_ROWS));


    // Draw Boxes
    g.setFont(box_font);
    for(int i=0;i<NUM_TOTAL;i++)
    {
		if(i != currbox || curr == null)
		{
			g.setColor(boxcolor);
			g.fillRect(boxes[i][0],boxes[i][1],BOX_WIDTH,BOX_HEIGHT);
			if(i == currbox)
			{
				g.setColor(outline);
				g.drawRect(boxes[i][0], boxes[i][1],BOX_WIDTH,BOX_HEIGHT);
			}
			g.setColor(numbers);
			if(boxes[i][2] < 10)
				g.drawString(String.valueOf(boxes[i][2]),boxes[i][0]+BSTR_X,boxes[i][1]+BSTR_Y);
			else
				g.drawString(String.valueOf(boxes[i][2]),boxes[i][0]+B2STR_X,boxes[i][1]+BSTR_Y);
		}
//		else if (curr != null)
//		{
//			g.setColor(outline);
//			g.drawRect(curr.x-xdiff,curr.y-ydiff,
//			BOX_WIDTH,BOX_HEIGHT);
//		}
	}

	if(curr != null)
	{
		g.setColor(selectedboxcolor);
		g.fillRect(curr.x-xdiff,curr.y-ydiff,BOX_WIDTH,BOX_HEIGHT);
		g.setColor(outline);
		g.drawRect(curr.x-xdiff,curr.y-ydiff,BOX_WIDTH,BOX_HEIGHT);
		
		g.setColor(numbers);
		if(boxes[currbox][2] < 10)
			g.drawString(String.valueOf(boxes[currbox][2]),curr.x-xdiff+BSTR_X,curr.y-ydiff+BSTR_Y);
		else
			g.drawString(String.valueOf(boxes[currbox][2]),curr.x-xdiff+B2STR_X,curr.y-ydiff+BSTR_Y);
	}

    // Draw YOU WIN!
    if (goal){
      g.setColor(goal_color);
      g.setFont(goal_font);
      g.drawString("It's a Magic Square!!", 50, 120);
    }

  }

  // This function is called when click on a level to reset and start
  // at that level.
  void update_level(int level) {
System.out.println("level: " + level);
    // Reset the variables.
    NUM_COLS = NUM_ROWS = row = level;
    NUM_TOTAL = NUM_COLS*NUM_ROWS;

    // Setup where to print the row_additions
    str_x = X_INSET + NUM_COLS * (BOX_WIDTH + BOX_GAP) +
      STR_X_GAP;
    str_y = Y_INSET;


    for(int i = 0;i < NUM_TOTAL;i++) {
      // Setup where boxes are First printed.
      boxes[i][0] = (int)(str_x + fm2.stringWidth
			  (longest_string) + STR_X_GAP +
			  (Math.floor(i/row) * (BOX_WIDTH + 15)));
      boxes[i][1] = str_y + ((i % row)*(BOX_HEIGHT+BOX_GAP));
      boxes[i][2] = i+1;
      // Setup where boxes can really go.
      grid_locs[i][0] = X_INSET + (i % NUM_COLS) *
	(BOX_WIDTH+BOX_GAP);
      grid_locs[i][1] = (int)(Y_INSET +
			      Math.floor(i/NUM_COLS) * (BOX_HEIGHT+BOX_GAP));
      grid_locs[i][2] = -1;
    }

    addup();

    repaint();

  }

  // This changes the Foreground or Background Color variable, and
  // re-draws the Frame
  void updatecolor(String choice, Color col) {
    if(choice.equals("fg"))
      fgmagcan = col;
    else if(choice.equals("bg"))
      bgmagcan = col;
    else if(choice.equals("box"))
      boxcolor = col;
    else if(choice.equals("outline"))
      outline = col;
    else if(choice.equals("numbers"))
      numbers = col;

    setBackground(bgmagcan);

    repaint();
  }

  void update_number(int number) {

    boxes[currbox][2] = number;
    addup();
    repaint();
  }

  public boolean addup() {

    // Sum the Rows
    for(int k = 0; k < NUM_ROWS; k++) {
      // Reset to 0.
      rows[k] = 0;
      for(int l = 0; l < NUM_ROWS; l++) {
	if(grid_locs[l+(k*NUM_ROWS)][2] == -1)
	  continue;
	else
	  rows[k] += boxes[
			   grid_locs[l+(k*NUM_ROWS)][2]][2];
      }
    }
    // Sum the Cols
    for(int k = 0;k < NUM_COLS; k++) {
      // Reset to 0.
      cols[k] = 0;
      for(int l = 0; l < NUM_COLS; l++) {
	if(grid_locs[k+(l*NUM_COLS)][2] == -1)
	  continue;
	else
	  cols[k] += boxes[
			   grid_locs[k+(l*NUM_COLS)][2]][2];
      }
    }
    // Sum Diag 1
    diags[1] = 0;
    for(int l = 0;l < NUM_COLS; l++){
      if(grid_locs[l*(NUM_COLS+1)][2] == -1)
	continue;
      else
	diags[1] += boxes[
			  grid_locs[l*(NUM_COLS+1)][2]][2];
    }

    // Sum Diag 2
    diags[0] = 0;
    for(int l = 0;l < NUM_COLS; l++){
      if(grid_locs[(l+1)*(NUM_COLS-1)][2] == -1)
	continue;
      else
	diags[0] += boxes[
			  grid_locs[(l+1)*(NUM_COLS-1)][2]][2];
    }

    return true;
  }

  public boolean goal_state() {

    goal = true;
    int number = diags[0];

    if(number == 0)
      goal = false;

    if(diags[0] != diags[1])
      goal = false;

    for(int i=0;i<NUM_ROWS;i++){
      if(rows[i] != number || cols[i] != number) {
	goal = false;
	break;
      }
    }
    return true;
  }

}



