import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.util.*;

public class WalkingApplet extends Applet {
  /***
    * Main Part of Walking Applet
    */
  WalkingPanel wpanel;

  /***
    * init the applet.
    */
  public void init() {
    /* Grids parameters */
    int num_x = 4;
    int num_y = 4;
    int grid_width = 16;
    int grid_height = 16;

    setLayout(new BorderLayout());

    String back_img_name = "initback.gif";
    String float_img_name = "float.gif";
    Image init_back_img = getImage(getCodeBase(), back_img_name);
    Image float_img = getImage(getCodeBase(), float_img_name);

    wpanel = new WalkingPanel(num_x, num_y, grid_width, grid_height);

    /* currently all backgrounds are same, initback.gif */
    for (int i=0;i<num_x;i++) {
      for (int j=0;j<num_y;j++) {
	wpanel.setBackImage(init_back_img, i, j);
      }
    }

    /* floater initial position (2, 2) */
    int float_i = 2;
    int float_j = 2;

    /* creating floater */
    Floater f = new Floater(float_img, 0, 0);
    wpanel.setFloater(f, float_i, float_j);

    wpanel.addKeyListener(new WalkingKeyListener(wpanel));

    add("Center", wpanel);

    //resize(num_x*grid_width, num_y*grid_height);
  }

  public void paint(Graphics g) {
    wpanel.paint(g);
  }
}

class WalkingPanel extends Panel
  implements Observer {

  int num_x, num_y;
  int grid_width, grid_hight;
  Grid grids[][];
  int float_i, float_j;
  Floater floater;

  WalkingPanel (int num_x, int num_y,
		int grid_width, int grid_height) {
    this.num_x = num_x;
    this.num_y = num_y;
    grids = new Grid[num_x][num_y];
    for (int i=0;i<num_x;i++) {
      for (int j=0;j<num_y;j++) {
	grids[i][j] = new Grid(i, j,
			       grid_width, grid_height);
	grids[i][j].addObserver(this);
      }
    }
    setSize(new Dimension(num_x*grid_width, num_y*grid_height));
  }

  void setBackImage(Image img, int i, int j) {
    grids[i][j].setBackImage(img);
  }

  void setFloater(Floater f, int i, int j) {
    floater = f;
    float_i = i;
    float_j = j;
    grids[i][j].addFloater(f);
  }

  void moveFloater(int next_i, int next_j) {
    grids[float_i][float_j].removeFloater(floater);
    grids[next_i][next_j].addFloater(floater);
    grids[float_i][float_j].notifyObservers();
    grids[next_i][next_j].notifyObservers();
    float_i = next_i;
    float_j = next_j;
  }


  void moveFloater(String direction) {
    if (direction == null) return;
    int next_i = 0, next_j = 0;
    if (direction.equals("UP")) {
      next_i = float_i;
      next_j = float_j - 1;
    } else if (direction.equals("DOWN")) {
      next_i = float_i;
      next_j = float_j + 1;
    } else if (direction.equals("LEFT")) {
      next_i = float_i - 1;
      next_j = float_j;
    } else if (direction.equals("RIGHT")) {
      next_i = float_i + 1;
      next_j = float_j;
    }

    System.out.println("Moving Floater: ("
		       + float_i + ", " + float_j + ") -> ("
		       + next_i + ", " + next_j + ")");
    if (next_i>=0 && next_i<num_x
	&& next_j>=0 && next_j<num_y) {
      moveFloater(next_i, next_j);
    }
  }

  public void update(Observable ob, Object arg) {
    Grid grid = (Grid)ob;
    System.out.println("WalkingPanel changed on grid ("
		       + grid.i + ", " + grid.j + ")");
    grid.paint(getGraphics(), this);
  }

  public void paint(Graphics g) {
    System.out.println("Painting all grids");
    for (int i=0;i<num_x;i++) {
      for (int j=0;j<num_y;j++) {
	System.out.print("(" + i + ", " + j + ") ");
	grids[i][j].paint(g, this);
      }
      System.out.println("");
    }
    System.out.println("done.");
  }
}

class Grid extends Observable {
  int i, j;
  Rectangle rect;
  Image back_img; 
  Floater floater;

  Grid(int i, int j, int width, int height) {
    int topleft_x = i*width;
    int topleft_y = j*height;
    this.rect = new Rectangle(topleft_x, topleft_y, width, height);
  }

  void setBackImage(Image back_img) {
    this.back_img = back_img;
    setChanged();
  }

  void addFloater(Floater floater) {
    this.floater = floater;
    setChanged();
  }

  void removeFloater(Floater floater) {
    this.floater = null;
    setChanged();
  }

  Rectangle getRectangle() {
    return rect;
  }

  Image getBackImage() {
    return back_img;
  }

  public void paint(Graphics g, Component obsvr) {
    g.drawImage(back_img, rect.x, rect.y, obsvr);
    if (floater != null) {
      g.drawImage(floater.getImage(),
		  rect.x+floater.getOffsetX(),
		  rect.y+floater.getOffsetY(),
		  obsvr);
    }
  }
}

class Floater {
  int offset_x, offset_y;
  Image img;

  Floater(Image img, int offset_x, int offset_y) {
    this.img = img;
    this.offset_x = offset_x;
    this.offset_y = offset_y;
  }

  Image getImage() {
    return img;
  }

  int getOffsetX() {
    return offset_x;
  }

  int getOffsetY() {
    return offset_y;
  }
}

class WalkingKeyListener implements KeyListener {
  WalkingPanel wpanel;

  WalkingKeyListener(WalkingPanel wpanel) {
    this.wpanel = wpanel;
  }

  public void keyTyped(KeyEvent e) {
    System.out.println("Typed -> " + e.getKeyChar());
  }

  public void keyPressed(KeyEvent e) {
    System.out.println("Pressed -> " + e.getKeyCode());
    String direction = null;
    if (e.getKeyCode() == KeyEvent.VK_DOWN) {
      direction = "DOWN";
      System.out.println("Down");
    } else if (e.getKeyCode() == KeyEvent.VK_UP) {
      direction = "UP";
      System.out.println("Up");
    } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
      direction = "LEFT";
      System.out.println("Left");
    } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
      direction = "RIGHT";
      System.out.println("Right");
    }
    wpanel.moveFloater(direction);
  }

  public void keyReleased(KeyEvent e) {
  }
}
