/**
@author Martin Klossek, Martin Meedt und Fabian Wleklinski
letzte Bearbeitung: 29.05.99 | 14:00 
*/

import java.awt.*;
import java.io.*;


/** Beinhaltet den Arbeitsbereich und auch die Programmeinstellungen des
Zeichenprogrammes */
public class TPaint {
  
  public TPaint() {
    FPaintWorkplace = new TPaintWorkplace();
    FPaintSettings = new TPaintSettings();
  }

  /** Liefert die Instanz der Programmeinstellungen zurück */
  public TPaintSettings PaintSettings() {
    return FPaintSettings;
  }

  /** Liefert die Instanz des Arbeitsbereiches zurück */
  public TPaintWorkplace PaintWorkplace() {
    return FPaintWorkplace;
  }

  /** Speichert den Arbeitsbereich und die Programmeinstellungen in einer Datei */
  public void saveToFile( String filename ) throws java.io.FileNotFoundException, java.io.IOException {
    FileOutputStream fileOutputStream = new FileOutputStream( filename );
    ObjectOutputStream objectOutputStream = new ObjectOutputStream( fileOutputStream );

    objectOutputStream.writeObject( FPaintWorkplace );
    objectOutputStream.writeObject( FPaintSettings );
    objectOutputStream.flush();

    fileOutputStream.close();
  }

  /** Restauriert den Arbeitsbereich und die Programmeinstellungen aus einer Datei */
  public void loadFromFile( String filename ) throws java.io.StreamCorruptedException, java.io.IOException, java.lang.ClassNotFoundException {
    FileInputStream fileInputStream = new FileInputStream( filename );
    ObjectInputStream objectInputStream = new ObjectInputStream( fileInputStream );

    FPaintWorkplace = (TPaintWorkplace) objectInputStream.readObject();
    FPaintSettings = (TPaintSettings) objectInputStream.readObject();

    fileInputStream.close();
  }

  // Der Arbeitsbereich
  private TPaintWorkplace FPaintWorkplace;

  // Die Programmeinstellungen
  private TPaintSettings FPaintSettings;
}

/** Beinhaltet einen Arbeitsbereich für beliebig viele Zeichenobjekte */
class TPaintWorkplace implements java.io.Serializable{

  /** Erzeugt einen Arbeitsbereich */
  public TPaintWorkplace() {
    // vererbten Konstruktor aufrufen
    super();

    // Den Container für die Elemente anlegen. Für das erste benutzen wir mal 10 Elemente
    items = new Object[ 10 ];
  }

  /** Markiert / Demarkiert alle Elemente */
  public void setSelected( boolean isSelected ) {
    for (int i = 0; i < FCount; i++) {
      ((TGraphic) items[ i ]).setSelected( isSelected );
    }
  }

  /** löscht alle Elemente */
  public void clear(  ) {
    for (int itemI=0; itemI < FCount; itemI++) {
      items[ itemI ] = null;
    }
    FCount = 0;
  }

  /** Fügt ein Grafikelement hinzu.
  @param item Das hinzuzufügende Grafikelement
  */
  public void add( TGraphic item ) {
    // sicherstellen, daß genügend Platz vorhanden ist
    ensureCapacity( FCount + 1 );
    // Das neue Element aufnehmen
    items[ FCount ] = item;
    // Elementzähler erhöhen
    FCount++;
  }

  /** Liefert ein Grafikelement zurück
  @param index Der Index des Grafikelementes
  */
  public TGraphic getItem( int index ) {
    TGraphic result = null;
    if (index < FCount) {
      result = (TGraphic) items[ index ];
    }

    return result;
  }

  /** Liefert den Index eines bestimmten Grafikelementes zurück
  @param item Das Grafikelement, für das der Index gesucht wird
  */
  public int indexOf( TGraphic graphic ) {
    int result = -1;
    for (int itemI=0; itemI < FCount; itemI++) {
      if ( ((TGraphic) items[ itemI ]) == graphic ) {
        result = itemI;
        break;
      }
    }
    return result;
  }

  /** löscht ein bestimmtes Grafikelement
  @param index Der Index des zu löschenden Elementes
  */
  public void delete( int index ) {
    // wenn Index außerhalb derGrenzen -> raus
    if ((index < 0) || (index >= FCount)) {
      return;
    }

    // Alle folgenden Elemente aufrücken
    for (int itemI = index; itemI < FCount-1; itemI++) {
      items[ itemI ] = items[ itemI+1 ];
    }

    FCount--;
  }

  /** löscht das angegebene Grafikelement
  @param Graphic Das zu löschende Elemente
  */
  public void delete( TGraphic Graphic ) {
    // Index des Elements bestimmen:
    int delResult = 0;

    for ( int itemI = 0; itemI < FCount; itemI++) {
      if (items[ itemI ] == Graphic) {
         delete (itemI);
         break; 
      } else if (items[ itemI ] instanceof TGraphicContainer) {
         delResult = ((TGraphicContainer) items[itemI]).delete(Graphic);
         if (delResult==0) { break; } // Ein Element in Container wurde gelöscht
         if (delResult==1) { delete (itemI); break; } // Das letzte Element des Containers wurde gelöscht
                                 // Container ohne Elemente werden nicht zugelassen, daher auch diesen löschen! 
      }
    }
 
  }

  /** Liefert die Anzahl der Elemente zurück, die im Moment vorhanden sind */
  public int count() {
    return FCount;
  }

  /* Stellt sicher, daß Platz für die übergebene Anzahl an Elementen ist. Wenn nicht,
  wird Platz geschaffen */
  private void ensureCapacity( int count ) {
    // Wenn genügend Platz ist -> raus
    if (items.length >= count) {
      return;
    }

    // Ein neues Array anlegen, mit der doppelten, gewünschten Größe
    Object[] tempArray = new Object[ count * 2 ];
    // Die bestehenden Elemente dort hineinkopieren
    System.arraycopy( items, 0, tempArray, 0, items.length );
    // Das temporäre Array übernehmen
    items = tempArray;
  }

  /* Hält die Anzahl der Elemente, die momentan zugewiesen sind */
  private int FCount;

  /* Hält alle Grafikelemente */
  public Object[] items;
}

/** Beinhaltet die Programmoptionen */
class TPaintSettings implements java.io.Serializable {
  /** Die Größe des Workplace */
  public double maxX, maxY; 
  /** Die Startkoordinaten des sichtbaren Workplace-Ausschnittes */
  public double mapStartX, mapStartY;
  /** Die Endkoordinaten des sichtbaren Workplace-Ausschnittes */
  public double Xratio, Yratio;
  /** Die Größe des Fensters */
  public int frameWidth, frameHeight;  
  /** Aktuelle Zeichenfarbe */
  public Color currentColor;
  /** Aktuelle Hintergrundfarbe */
  public Color currentBGColor;

  /** Aktuelle Markerfarbe */
  private Color FMarkerColor;
  /** Toleranz beim Selektieren */
  private double FToleranz;


 /** Liefert die aktuelle Objektzeichenfarbe zurück
   * @return Color Die aktuelle Objektzeichenfarbe
   */
 public Color getCurrentColor () {
    return (currentColor);
 }

 /** Liefert die aktuelle Markerfarbe zurück
   * @return Color Die aktuelle Markefarbe
   */
 public Color getMarkerColor() {
    return (FMarkerColor);
 }
 /** Setzt die aktuelle Markerfarbe 
   * @param Color Die aktuelle Markefarbe
   */
 public void setMarkerColor(Color MarkerColor) {
    FMarkerColor = MarkerColor;
 }

 /** Liefert die aktuelle Selektiertoleranz zurück
   * @return int Die aktuelle Toleranz als double
   */
 public double getToleranz() {
    return (FToleranz);
 }
 /** Setzt die aktuelle Selektiertoleranz
   * @param Color Die Toleranz in Pixel ausgedrückt
   */
 public void setToleranz(int Toleranz) {
    FToleranz = double2pixWidth (Toleranz);
 }


 /** Rechnet Reele Zahl aus Koordinatensystem in X-Pixel um 
   * @param double Der Wert in Reeler Zahl, der in Pixel umgerechnet werden soll
   */
 public int double2pixX ( double x ) {
    int w = (int) ( (x - mapStartX) * Xratio );
    return (w);
 }

 /** Rechnet Reele Zahl aus Koordinatensystem in Y-Pixel um 
   * @param double Der Wert in Reeler Zahl, der in Pixel umgerechnet werden soll
   */
 public int double2pixY ( double y ) {
    int w = (int) ( (y - mapStartY) * Yratio );
    return (w);
 }

 /** Rechnet Breite als Reele Zahl aus Koordinatensystem in Pixelanzahl um 
   * @param double Der Wert in Reeler Zahl, der in Pixel umgerechnet werden soll
   */
 public int double2pixWidth ( double x ) {
    int w = (int) ( x * Xratio );
    return (w);
 }
 /** Rechnet Reele Zahl aus Koordinatensystem in Y-Pixel um 
   * @param double Der Wert in Reeler Zahl, der in Pixel umgerechnet werden soll
   */
 public int double2pixHeight ( double y ) {
    int w = (int) ( y * Yratio );
    return (w);
 }

 /** Rechnet X-Pixelangabe in Reele Zahl des Koordinatensystems um
   * @param int Der Pixel, der in die reele Zahl umgerechnet werdeb soll
   */
 public double pix2doubleX ( int x ) {
    double w = (double) ( (x / Xratio) + mapStartX );
    return (w);
 }
 /** Rechnet Y-Pixelangabe in Reele Zahl des Koordinatensystems um
   * @param int Der Pixel, der in die reele Zahl umgerechnet werdeb soll
   */
 public double pix2doubleY ( int y ) {
    double w = (double) ( (y / Yratio) + mapStartY );
    return (w);
 }

 /** Rechnet X-Pixel in reelen Abstand im Koordinatensystems um
   * @param int Die Pixel, die umgerechnet werden sollen
   */
 public double pix2doubleWidth ( int x ) {
    double w = (double) (x / Xratio);
    return (w);
 } 
 /** Rechnet Y-Pixel in reelen Abstand im Koordinatensystems um
   * @param int Die Pixel, die umgerechnet werden sollen
   */
 public double pix2doubleHeight ( int y ) {
    double w = (double) (y / Yratio);
    return (w);
 } 

 /** Vergrößert oder verkleinert den sichtbaren Zeichen-Ausschnitt
   * @param double Vergrößerung für Werte > 1, Verkleinerung < 1
   */
 public void zoom ( double ZoomFaktor ) {
    Xratio = Xratio * ZoomFaktor;
    if ( double2pixX (maxX)+1 < frameWidth -1 ) {
       mapStartX = 0;
    }

    Yratio = Yratio * ZoomFaktor;
    if ( double2pixY (maxY)+1 < frameHeight -1 ) {
       mapStartY = 0;
    }
 }

 /** Bewegt den sichtbaren Zeichen-Ausschnitt
   * @param int Bewegung in X-Richtung
   * @param int Bewegung in Y-Richtung
   */   
 public void move ( int xMove, int yMove ) {

    if ( double2pixX (maxX)+1 > frameWidth -1 ) {
       mapStartX += (xMove) / Xratio;
       if (mapStartX < 0) { mapStartX = 0; }
       if (mapStartX + 1 + (frameWidth / Xratio) > maxX) { 
          mapStartX = maxX - (frameWidth / Xratio);
       }
    }
         
    if ( double2pixY (maxY)+1 > frameHeight -1 ) {
       mapStartY += (yMove) / Yratio;
       if (mapStartY < 0) { mapStartY = 0; }
       if (mapStartY + 1 + (frameHeight / Yratio) > maxY) { 
          mapStartY = maxY - (frameHeight / Yratio);
       }
    }  

 }

 /** Liefert den größten X-Pixel zurück, denn ein Objekt im 
   * sichtbaren Ausschnitt belegen kann
   * @return int Wert für X in Pixeln 
   */
 public int getPixelMaxX () {
   return ( double2pixX (maxX) );
 }

 /** Liefert den größten Y-Pixel zurück, denn ein Objekt im 
   * sichtbaren Ausschnitt belegen kann
   * @return int Wert für Y in Pixeln 
   */
 public int getPixelMaxY () {
   return ( double2pixY (maxY) );
 }


} 