import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Image;
import java.text.ParsePosition;
import java.text.NumberFormat;
import java.lang.Character;
import java.io.*;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;

import Coordinate;
import SommerCampWrap;


// Gitter-Canvas ermöglicht das editieren eines Gitters
class GridCanvas extends Canvas implements MouseListener,MouseMotionListener
{
  private static final int R = 2;
  private int w, h, snap_i, snap_j;
  private Color gridcolor;
  private Coordinate[][] coord;
  private Image bufferImage, bgImage;
  private Graphics bG;

  public GridCanvas(int w, int h, Image bgImage, Color gridcolor)
  {
    // Initialisierung (mit Rand der Breite R)
    this.w = w;
    this.h = h;
    this.bgImage = bgImage;
    this.gridcolor = gridcolor;
    coord = null;
    snap_i = -1;	// Bedeutet: kein Gitterpunkt geschnappt
    addMouseListener(this);
    addMouseMotionListener(this);
  }

  public Dimension getPreferredSize()
  {
    return getMinimumSize();
  }

  public synchronized Dimension getMinimumSize()
  {
    return new Dimension(w+2*R, h+2*R+1);	// Rand der Breite R
    // Am oberen Rand eine Bildzeile zusätzlich Platz lassen
  }

  public void paint(Graphics g)
  {
    update(g);
  }

  public void update(Graphics g)
  {
    if (bG == null)
      {
	// Double-Buffering starten
	bufferImage = createImage(w+2*R, h+2*R);
	bG = bufferImage.getGraphics();
      }

    bG.setColor(Color.white);
    bG.fillRect(0, 0, w+2*R, h+2*R);
    if (bgImage != null)
      // Bild zeichnen, falls vorhanden
      bG.drawImage(bgImage, R, R, this);
    if (coord != null)
      {
	// Gitter drüberzeichnen, falls vorhanden (dabei auf Rand achten)
	int i,j;

	bG.setColor(gridcolor);
	for (i=0; i<coord.length; i++)
	  {
	    for(j=0; j<coord[i].length; j++)
	      {
		bG.drawOval(coord[i][j].x, coord[i][j].y, 2*R, 2*R);
		if (j>0)
		  bG.drawLine(R+coord[i][j].x, R+coord[i][j].y,
			      R+coord[i][j-1].x, R+coord[i][j-1].y);
		if (i>0)
		  bG.drawLine(R+coord[i][j].x, R+coord[i][j].y,
			      R+coord[i-1][j].x, R+coord[i-1][j].y);
	      }
	  }
      }

    // Double-Buffer ausgeben
    // Am oberen Rand eine Bildzeile Platz lassen
    g.drawImage(bufferImage, 0, 1, this);
  }

  public void NewCoordinate(int xx, int yy)
  {
    // Neues Gitter initialisieren
    int i,j;

    coord = new Coordinate[yy][xx];
    for (i=0; i<yy; i++)
      for (j=0; j<xx; j++)
	coord[i][j] = new Coordinate(j*(w-1)/(xx-1), i*(h-1)/(yy-1));
    repaint();
  }

  public Coordinate[][] GetCoordinate()
  {
    // Kopie des aktuellen Gitters ausgeben
    int i,j;
    Coordinate[][] newcoord = null;

    if (coord != null)
      {
	newcoord = new Coordinate[coord.length][coord[0].length];
	for (i=0; i<coord.length; i++)
	  for (j=0; j<coord[i].length; j++)
	    newcoord[i][j] = new Coordinate(coord[i][j]);
      }

    return newcoord;
  }

  public void mousePressed(MouseEvent e)
  {
    // Gitterpunkt schnappen, falls nahe genug dran
    int i,j;

    if (coord != null)
      {
	for (i=0; i<coord.length; i++)
	  for(j=0; j<coord[i].length; j++)
	    if ( (e.getX()-R-coord[i][j].x)*(e.getX()-R-coord[i][j].x) +
		 (e.getY()-R-coord[i][j].y)*(e.getY()-R-coord[i][j].y) 
		 <= 2*R*R )
	      {
		snap_i = i;
		snap_j = j;
	      }
      }
  }

  public void mouseReleased(MouseEvent e)
  {
    // Geschnappten Gitterpunkt aufgeben (falls vorhanden)
    snap_i = -1;
  }

  public void mouseExited(MouseEvent e)
  {
    // Geschnappten Gitterpunkt aufgeben (falls vorhanden)
    snap_i = -1;
  }

  public void mouseEntered(MouseEvent e) {}
  public void mouseClicked(MouseEvent e) {}
  public void mouseMoved(MouseEvent e) {}

  public void mouseDragged(MouseEvent e)
  {
    int x,y;

    if (snap_i>=0)  // Prüfen ob ein Gitterpunkt geschnappt wurde
      {
	// Gitterpunkt bewegen (auf Offset achten)
	x = e.getX() - R;
	y = e.getY() - R;
	if ( (snap_j>0) && (snap_j<coord[snap_i].length-1) )
	  {
	    if (x<0)  x = 0;
	    if (x>=w) x = w-1;
	    coord[snap_i][snap_j].x = x;
	  }
	if ( (snap_i>0) && (snap_i<coord.length-1) )
	  {
	    if (y<0)  y = 0;
	    if (y>=h) y = h-1;
	    coord[snap_i][snap_j].y = y;
	  }
	repaint();
      }
  }

  public void loadGrid(SommerCampWrap applet, String filename)
  {
    if (coord==null)
      return;

    try
      {
	FileReader fr = new FileReader(filename);
	StreamTokenizer st = new StreamTokenizer(fr);
	Coordinate max, min, load[][];
	int i, j, k, l;
	
	// StreamTokenizer initialsieren
	st.parseNumbers();

	i = j = 0;
	for (l=0; l<2; l++)
	  {
	    if (st.nextToken() != StreamTokenizer.TT_NUMBER)
	      { 
		applet.showStatus("Dateiformatfehler in Zeile " +
				  st.lineno() + ": Gitter nicht geladen");
		fr.close(); return;
	      }
	    if (l==0)
	      i = (int)(st.nval + 0.5);
	    else
	      j = (int)(st.nval + 0.5);
	  }
	// Größe überprüfen
	load = GetCoordinate();
	if ((load.length != i) || (load[0].length != j))
	  {
	    applet.showStatus("Gittergröße stimmt nicht überein");
	    fr.close(); return;
	  }
	max = new Coordinate(Integer.MIN_VALUE, Integer.MIN_VALUE);
	min = new Coordinate(Integer.MAX_VALUE, Integer.MAX_VALUE);
	for (i=0; i<load.length; i++)
	  {
	    for (j=0; j<load[i].length; j++)
	      {
		for (l=0; l<2; l++)
		  {
		    if (st.nextToken() != StreamTokenizer.TT_NUMBER)
		      { 
			applet.showStatus("Dateiformatfehler in Zeile " +
					  st.lineno() +
					  ": Gitter nicht geladen");
			fr.close(); return;
		      }
		    if (l==0)
		      load[i][j].x = (int)(st.nval + 0.5);
		    else
		      load[i][j].y = (int)(st.nval + 0.5);
		  }
		if (max.x < load[i][j].x)  max.x = load[i][j].x;
		if (min.x > load[i][j].x)  min.x = load[i][j].x;
		if (max.y < load[i][j].y)  max.y = load[i][j].y;
		if (min.y > load[i][j].y)  min.y = load[i][j].y;
	      }
	  }
	fr.close();
	// Gitter skalieren
	for (i=0; i<load.length; i++)
	  for (j=0; j<load[i].length; j++)
	    {
	      load[i][j].x = (w-1)*(load[i][j].x - min.x)/(max.x - min.x);
	      load[i][j].y = (h-1)*(load[i][j].y - min.y)/(max.y - min.y);
	      // Gitterrand auf Bildrand setzen
	      if (j==0) load[i][j].x = 0;
	      if (j==load[i].length-1) load[i][j].x = w-1;
	      if (i==0) load[i][j].y = 0;
	      if (i==load.length-1) load[i][j].y = h-1;
	    }
	coord = load;
	applet.showStatus("Gitter geladen");
	repaint();
      }
    catch (FileNotFoundException e)
      {
	applet.showStatus("Datei nicht gefunden");
      }
    catch (IOException e)
      {
	applet.showStatus("Fehler beim Lesen der Datei");
      }
  }

  private void writeInt(PrintWriter pw, int i)
  {
    String s = "   " + i;
    try
      {
	if (s.length()>6)
	  pw.print(s.substring(2));
	else
	  pw.print(s.substring(s.length()-4));
      }
    catch (StringIndexOutOfBoundsException e) {}
  }

  public void saveGrid(SommerCampWrap applet, String filename)
  {
    if (coord==null)
      return;

    try
      {
        FileWriter fw = new FileWriter(filename);
	PrintWriter pw = new PrintWriter(fw);
	int i, j;

	writeInt(pw, coord.length);
	writeInt(pw, coord[0].length);
	pw.println();
	for (i=0; i<coord.length; i++)
	  {
	    for (j=0; j<coord[i].length; j++)
	      {
		writeInt(pw, coord[i][j].x);
		writeInt(pw, coord[i][j].y);
		if (j<coord[i].length-1)
		  pw.print("  ");
		else
		  pw.println();
	      }
	  }
	pw.close();
	applet.showStatus("Gitter gespeichert");
      }
    catch (FileNotFoundException e)
      {
	applet.showStatus("Datei nicht gefunden");
      }
    catch (IOException e)
      {
	applet.showStatus("Fehler beim Schreiben der Datei");
      }

  }
}
