
import java.awt.*;
import java.util.*;
//import java.lang.reflect.*;
import java.io.*;

public class MapGraphics extends Canvas {
  int mapxsize;
  int mapysize;

  int xsize;
  int ysize;

  int cellsize = 4;

  double magnification;

  Color grid_color;
  Color border_color;
  Color background_color;
  Color[][] pixmap;

  boolean grid_enabled;
  boolean border_enabled;

  public MapGraphics(int width, int height)
  {
    int i, j;

    mapxsize = width;
    mapysize = height;
    magnification = 1.0;

    xsize = (cellsize+1)*width+3;
    ysize = (cellsize+1)*height+3;

    pixmap = new Color[height][width];

    background_color = Color.WHITE;

    for (i = 0; i < height; i++)
      for (j = 0; j < width; j++)
        pixmap[i][j] = background_color;

    grid_color = Color.GRAY;
    border_color = Color.BLACK;

    grid_enabled = true;
  }

  public void setGridColor(Color color)
  {
    grid_color = color;
  }

  public void setBorderColor(Color color)
  {
    border_color = color;
  }

  public void setBackgroundColor(Color color)
  {
    background_color = color;
  }

  public void enableBorder()
  {
    border_enabled = true;
  }

  public void disableBorder()
  {
    border_enabled = false;
  }

  public void enableGrid()
  {
   grid_enabled = true;
  }

  public void disableGrid()
  {
    grid_enabled = false;
  }

  public void drawGrid(Graphics g)
  {
    int r, c;

    g.setColor(grid_color);

    for (c = 2+cellsize; c < xsize-2; c += cellsize+1)
      g.drawLine(c, 2, c, ysize-3);

    for (r = 2+cellsize; r < ysize-2; r += cellsize+1)
      g.drawLine(2, r, xsize-3, r);
  }

  public void drawBorder(Graphics g)
  {
    g.setColor(border_color);
    if (grid_enabled) {
      g.drawLine(1, 1, xsize-2, 1);
      g.drawLine(1, ysize-2, xsize-2, ysize-2);
      g.drawLine(1, 1, 1, ysize-2);
      g.drawLine(xsize-2, 1, xsize-2, ysize-2);
    }
    else {
      g.drawLine(0, 0, xsize-2, 0);
      g.drawLine(0, ysize-2, xsize-2, ysize-2);
      g.drawLine(0, 0, 0, ysize-2);
      g.drawLine(xsize-2, 0, xsize-2, ysize-2);
    }
  }

  public void drawMap(Graphics g)
  {
    int r, c;

    if (grid_enabled) {
      for (r = 0; r < mapysize; r++) {
        for (c = 0; c < mapxsize; c++) {
          g.setColor(pixmap[r][c]);
          g.fillRect(2+(cellsize+1)*c, 2+(cellsize+1)*r, cellsize, cellsize);
        }
      }
    }
    else {
      for (r = 0; r < mapysize; r++) {
        for (c = 0; c < mapxsize; c++) {
          g.setColor(pixmap[r][c]);
          g.fillRect(1+(cellsize+1)*c, 1+(cellsize+1)*r, cellsize+1, cellsize+1);
        }
      }
    }

  }

  private boolean wipeMap(Graphics g)
  {
    g.setColor(background_color);
    g.fillRect(0, 0, xsize, ysize);
    return(true);
  }

  public void placeObject(int x, int y)
  {
    pixmap[y][x] = Color.BLACK;
  }

  public void placeObject(int x, int y, Color color)
  {
    pixmap[y][x] = color;

    repaint();
  }

  public void placeLine(int x1, int y1, int x2, int y2)
  {
    placeLine(x1, y1, x2, y2, Color.BLACK);
  }

  public void placeLine(int x1, int y1, int x2, int y2, Color color)
  {
    int x, y;
    int i, j;
    int t;
    double m;
    int minx, miny, maxx, maxy;

    i = mapxsize*y1+x1;
    j = mapxsize*y2+x2;
    if (i > j) {
      x = x1;
      y = y1;
      x1 = x2;
      y1 = y2;
      x2 = x;
      y2 = y;
    }

    System.out.println("(" + x1 + "," + y1 + ") -> (" + x2 + "," + y2 + ")");

    if (y1 == y2) {
      //Horizontal line
      for (x = x1; x <= x2; x++)
        pixmap[y1][x] = color;
    }
    else if (x1 == x2) {
      //Vertical line
      for (y = y1; y < y2; y++)
        pixmap[y][x1] = color;
    }
    else {
      //Sloped line
      m = 1.0*(y2-y1)/(x2-x1);
      System.out.println("m = " + m);
      if (Math.abs(m) <= 1) {
        System.out.println("small slope");
        if (x1 < x2) {
          minx = x1;
          maxx = x2;
          miny = y1;
        }
        else {
          minx = x2;
          maxx = x1;
          miny = y2;
        }
        for (t = 0, x = minx; x <= maxx; x++, t++) {
          pixmap[miny+(int)(t*m)][x] = color;
        }
      }
      else {
        System.out.println("big slope");
        m = 1.0/m;
        if (y1 < y2) {
          miny = y1;
          maxy = y2;
          minx = x1;
        }
        else {
          miny = y2;
          maxy = y1;
          minx = y2;
        }
        for (t = 0, y = miny; y <= maxy; y++, t++) {
          pixmap[y][minx+(int)(t*m)] = color;
        }
      }
    }

    repaint();
  }

  public void loadMap(int[][] data)
  {
    loadMap(data, Color.BLACK);
  }

  public void loadMap(int[][] data, Color color)
  {
    int x, y;

    for (y = 0; y < mapysize; y++) {
      for (x = 0; x < mapxsize; x++) {
        pixmap[y][x] = (data[y][x] != 0 ? color : background_color);
      }
    }

    repaint();
  }

  public void loadMap(int[] data)
  {
    loadMap(data, Color.BLACK);
  }

  public void loadMap(int[] data, Color color)
  {
    int x, y;

    for (y = 0; y < mapysize; y++) {
      for (x = 0; x < mapxsize; x++) {
        pixmap[y][x] = (data[y*mapxsize+x] != 0 ? color : background_color);
      }
    }

    repaint();
  }

  public void loadMapMagnitude(int[][] data)
  {
    loadMapMagnitude(data, 0, 255);
  }

  public void loadMapMagnitude(int[][] data, int minval, int maxval)
  {
    int x, y;
    int i;
    float t, diff;

    if (maxval < minval)
      return;

    Color[] colors = new Color[maxval-minval];

    if (minval == maxval) {
      colors[0] = Color.BLACK;
    }
    else {
      diff = (float)1.0/maxval-minval;
      for (t = 0, i = minval; i <= maxval; i++, t += diff)
        colors[i-minval] = new Color(t, t, t);
    }

    for (y = 0; y < mapysize; y++) {
      for (x = 0; x < mapxsize; x++) {
        pixmap[y][x] = colors[data[y][x]-minval];
      }
    }

    repaint();
  }

  public void loadMapMagnitude(int[] data)
  {
    loadMapMagnitude(data, 0, 255);
  }

  public void loadMapMagnitude(int[] data, int minval, int maxval)
  {
    int x, y;
    int i;
    float t, diff;

    if (maxval < minval)
      return;

    Color[] colors = new Color[maxval-minval+1];

    if (minval == maxval) {
      colors[0] = Color.BLACK;
    }
    else {
      diff = (float)1.0/maxval-minval;
      for (t = 0, i = minval; i <= maxval; i++, t += diff)
        colors[i-minval] = new Color(t, t, t);
    }

    for (y = 0; y < mapysize; y++) {
      for (x = 0; x < mapxsize; x++) {
        pixmap[y][x] = colors[data[y*mapxsize+x]-minval];
      }
    }

    repaint();
  }

  public void paint(Graphics g) {
    long start, end;

//    start = System.currentTimeMillis();
    this.wipeMap(g);
    this.drawBorder(g);
    this.drawGrid(g);
    this.drawMap(g);
//    end = System.currentTimeMillis();

//    System.out.println("paint took " + (end-start) + "ms");
    System.out.println("painted");
  }

  public Dimension getPreferredSize() {
    return new Dimension(xsize, ysize);
  }

  public Frame createFrame(String title)
  {
    Frame frame = new Frame();
    frame.setTitle(title);
    frame.add(this);
    frame.pack();
    frame.setVisible(true);

    return frame;
  }

  public static void main(String[] argv) {
    int h = 100;
    int w = 200;
    Frame frame = new Frame();
    MapGraphics mg = new MapGraphics(w, h);
    int[] testdata = new int[w*h];
    for (int i = 0; i < w*h; i++) {
//      testdata[i] = (int)(Math.sin(1.0*i/(w*h)*4.0*Math.PI)*127.0+128.0);
      testdata[i] = (int)(i*255.0/(w*h));
    }
    mg.loadMapMagnitude(testdata, 0, 255);
    frame.add(mg);
    frame.pack();
    frame.setVisible(true);
    while (true) {
      double k = 0;
      for (int i = 0; i < w*h; i++) {
        testdata[i] = (int)(Math.sin(1.0*i/(w*h)*4.0*Math.PI+k)*127.0+128.0);
      }
      mg.loadMapMagnitude(testdata, 0, 255);
      k += Math.PI/50.0;
      mg.repaint();
      try { Thread.sleep(100); } catch (Exception e) { }
      System.out.println("called repaint");
    }
  }

}

