/**
 * english, without mouseEvents, perhaps no filled turtle
 * @author (Alfred Hermes) && AAK MV
 * @version (6a, 14.12.2003) && 08.12.2011
 */

import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
import java.awt.image.*;



@SuppressWarnings("unchecked")

public class AbiTurtle extends JFrame // implements ActionListener, MouseListener, MouseMotionListener      //Änderung AAK !!
{
    private int delay=1, lastX=0, lastY=0, colorValue = 0;
    private double x = 0, startX=0, y = 0, startY=0, direction = 0;
    private boolean down=true, isVisible; //the turtle is visible as triangle;
    private boolean activ; //created, but deactivated by clearScreen
    private Color tColor = Color.black;
    final int maxWidth = 1280, maxHeight = 1024;
    static int tNumber;
    static int barHeight=20; //height of the menubar
    static int currentWidth=500, currentHeight=250;     //Änderung AAK !! hier Fenstergröße einstellen!
    static boolean firstCall=true;
    static final Vector tList=new Vector();
    static JFrame jFrame;
    static Container container;
    public static Color[] colors = {Color.black,Color.green,Color.blue,Color.yellow,Color.red,Color.gray,Color.lightGray,Color.orange,Color.white};
    public static final int black=0, green=1, blue=2, yellow=3, red=4, gray=5, lightGray=6, orange=7, white=8;
    public static int[] rgbColor={-16777216,-16711936,-16776961,-256,-65536,-8355712,-4144960,-14336,-1};
    static Graphics g, gWithTurtle, gWithoutTurtle;
    static BufferedImage iWithTurtle, iWithoutTurtle;
    static JMenuBar menueBar;
    static JButton eraseButton, restoreButton, closeButton;
  
    //----------------- Konstruktoren --------------------------

    //--------------constructors------------------------
    
    private static boolean isFilled = true;                        //#6#//
    public static void setFilled(boolean filled){isFilled = filled;} //#6#//
    
    public AbiTurtle(){this(currentWidth,currentHeight);}
    public  AbiTurtle(int b, int h){
         if(firstCall) { 
          firstCall = false;
          jFrame=null; g=null; container=null;
          initComponents(b+12, h+barHeight+36);
          initGraphics();
          }
      if (jFrame == null || !jFrame.isVisible()){
          jFrame=null; g=null; container=null; removeTurtles();
          if(h == currentHeight) initComponents(b,h);
           else  initComponents(currentWidth,currentHeight); 
         initGraphics();
      }        
      double xx=currentWidth/2;
      double yy= (currentHeight-barHeight)/2;
      x=xx; y=yy;
      startX=x; startY=y;
      lastX= (int)x; lastY= (int)y;
      direction=0;
      tColor = Color.black;
      isVisible=false;
      activ=true;
      tList.add(this);
   }

//--------- administration -------------------------------------------------

private void initGraphics(){
   if (g==null){
     try {
        g = container.getGraphics();
        iWithTurtle = new BufferedImage(maxWidth, maxHeight, BufferedImage.TYPE_INT_RGB);
        iWithoutTurtle = new BufferedImage(maxWidth, maxHeight, BufferedImage.TYPE_INT_RGB);
        gWithTurtle = iWithTurtle.getGraphics();
        gWithoutTurtle = iWithoutTurtle.getGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, maxWidth, maxHeight);
        g.setColor(Color.black);
        gWithoutTurtle.setColor(Color.white);
        gWithoutTurtle.fillRect(0, 0, maxWidth, maxHeight);
        gWithoutTurtle.setColor(Color.black);
        gWithTurtle.setColor(Color.white);
        gWithTurtle.fillRect(0, 0, maxWidth, maxHeight);
        gWithTurtle.setColor(Color.black);
     }
     catch (Exception e){
       System.out.println("Error in initGraphics()");
       initComponents(currentWidth, currentHeight);
       g=container.getGraphics();
    }
  }
}

/**deletes the list of turtles*/
private void removeTurtles(){
   if (tList!= null){
     tList.removeAllElements();
     //listeners.removeAllElements();   //#6#    //Änderung AAK !!
   }
}

/** paints the turtle */
private synchronized void restoreView(){
   if (!jFrame.isVisible()){jFrame.setVisible(true);}
   if(g==null) initGraphics();
   activ=true;
   if(!jFrame.isVisible()){
      initComponents(currentWidth,currentHeight);
      jFrame.setSize(currentWidth,currentHeight);
      jFrame.setVisible(true);
      container=this.getContentPane();
      g=container.getGraphics();
      g.setClip(0,0,currentWidth,currentHeight);
      g.drawImage(iWithoutTurtle,0,0,container);
      menueBar.setVisible(true);
      //show all turtles
      for(int k=0; k<tList.size(); k++){
        AbiTurtle turtle = (AbiTurtle) tList.elementAt(k);
        if (turtle.isVisible && turtle.activ) paintTurtle(g, turtle);
      }//for
   }
   if (isVisible){
     if (delay<2) delay = 2;
     int x0= Math.min(lastX, (int)x);
     int y0= Math.min(lastY, (int)y);
     int b0=Math.abs((int)(x-lastX));
     int h0= Math.abs((int)(y-lastY));
     gWithTurtle.setClip(x0-21,y0-21,b0+42,h0+42);
     gWithTurtle.drawImage(iWithoutTurtle,0,0,container);
     tNumber = tList.size();
     for(int k=0; k<tNumber; k++){
      AbiTurtle tu = (AbiTurtle) tList.elementAt(k);
      if (tu.isVisible && tu.activ)
         paintTurtle(gWithTurtle, tu);
     }//for
     g.setClip(gWithTurtle.getClip());
     g.drawImage(iWithTurtle,0,0,container);
     g.setClip(0,0,currentWidth,currentHeight);  
  }//if
  if (delay >0 ) {
   try { Thread.sleep(delay);}
   catch (Exception e){}
  }
}

private void paintTurtle(Graphics grafics, AbiTurtle turtle){
  int[] fx = new int[3];
  int[] fy = new int[3];
  double td = turtle.direction;
  double x = turtle.x;
  double y = turtle.y;
  Color tColor = turtle.tColor;
  fx[0] = (int) (x + Math.cos(arc(td))*20);
  fy[0] = (int) (y + Math.sin(arc(td))*20);
  td += 90;
  fx[1] = (int) (x + Math.cos(arc(td))*5);
  fy[1] = (int) (y + Math.sin(arc(td))*5);
  td += 180;
  fx[2] = (int) (x + Math.cos(arc(td))*5);
  fy[2] = (int) (y + Math.sin(arc(td))*5);
  grafics.setColor(tColor);
  if(isFilled) grafics.fillPolygon(fx,fy,3); else grafics.drawPolygon(fx,fy,3);   //#6#//
}

private void testVisibility(){      //#6#//stattt examine
 try{
  if (!jFrame.isVisible()){jFrame.setVisible(true);}
  if(g==null || gWithoutTurtle==null) {g=null; initGraphics();}
 } catch (Exception e){System.out.println("Error showing window!");}
}
//Änderung AAK !!
public void actionPerformed(ActionEvent evt){
  Object obj = evt.getSource();
//  if (obj == eraseButton) clearScreen();
//    else if (obj == restoreButton) restoreWindow();
//      else 
      if (obj == closeButton) hideFrame();
}

/**
   * clears the Screen, not the informations about the turtles
*/
public void clearScreen(){              //#6#
  try {
    if (!jFrame.isVisible()){jFrame.setVisible(true);}
      g.setColor(Color.white);
      g.fillRect(0, 0, currentWidth, currentHeight);
      g.setColor(Color.black);
      gWithoutTurtle.setClip(0,0,maxWidth, maxHeight);
      gWithoutTurtle.setColor(Color.white);
      gWithoutTurtle.fillRect(0, 0, maxWidth, maxHeight);
      gWithoutTurtle.setColor(Color.black);
      gWithTurtle.setClip(0,0,maxWidth, maxHeight);
      gWithTurtle.setColor(Color.white);
      gWithTurtle.fillRect(0, 0, maxWidth, maxHeight);
      gWithTurtle.setColor(Color.black);
      // deactivate the turtles
      for(int k=0; k<tList.size(); k++){
        AbiTurtle tu = (AbiTurtle) tList.elementAt(k);
        if (tu.isVisible)
          tu.activ = false;
      }//for
  } catch (Exception e){};
}

protected void hideFrame(){
  jFrame.setVisible(false);
}

public void restoreWindow(){
  try {
    gWithTurtle.setClip(0,0,currentWidth,currentHeight);
    gWithoutTurtle.setClip(0,0,currentWidth,currentHeight);
    g.setClip(0,0,currentWidth,currentHeight); //may be not necessary
    g.drawImage(iWithoutTurtle,0,0,container);
    //show the turtles -------------
    tNumber = tList.size();
    for(int k=0; k<tNumber; k++){
     AbiTurtle turtle = (AbiTurtle) tList.elementAt(k);
     if (turtle.isVisible && turtle.activ) paintTurtle(g, turtle);
    }//for
 } catch (Exception e){}
}

private void initComponents(int b, int h){
   if (container==null) container = this.getContentPane();
   container.setBackground(Color.white);
   currentWidth=b;
   currentHeight=h;
   jFrame = this;
   jFrame.setTitle("AbiTurtle AAK Informatik MV/Turtle V.6a (c) Alfred Hermes");
    menueBar = new JMenuBar();
    jFrame.setJMenuBar(menueBar);
//    eraseButton = new JButton("Fenster löschen");
//    menueBar.add(eraseButton);     //Änderung AAK !!
//    eraseButton.addActionListener(new ActionListener() {
//       public void actionPerformed(ActionEvent evt) {
//         eraseButton_ActionPerformed(evt);
//       }
//     });
//    restoreButton = new JButton("Fenster wiederherstellen");
//    menueBar.add(restoreButton);      //Änderung AAK !!
//    restoreButton.addActionListener(new ActionListener() {
//       public void actionPerformed(ActionEvent evt) {
//         restoreButton_ActionPerformed(evt);
//       }
//     });
    closeButton = new JButton("Fenster schließen");
    menueBar.add(closeButton);      //Änderung AAK !!
    closeButton.addActionListener(new ActionListener() {
       public void actionPerformed(ActionEvent evt) {
         closeButton_ActionPerformed(evt);
       }
     });
   jFrame.setSize(currentWidth,currentHeight); // +barHeight);
   jFrame.setVisible(true);
   jFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
   
//   container.addMouseListener(this);        //Änderung AAK !!
//   container.addMouseMotionListener(this);   //Änderung AAK !!
}// initComponents

public void closeButton_ActionPerformed(ActionEvent evt) {
    hideFrame();
  }
  public void eraseButton_ActionPerformed(ActionEvent evt) {
    clearScreen();
  }
  public void restoreButton_ActionPerformed(ActionEvent evt) {
    restoreWindow();
  }
  
public void paint(Graphics gr){
  try {
    currentWidth = this.getWidth();
    currentHeight = this.getHeight();
    container = this.getContentPane();
    g=container.getGraphics();
    paintComponents(gr);
    restoreWindow();
    if (iWithoutTurtle == null) {
       iWithTurtle = new BufferedImage(maxWidth, maxHeight,BufferedImage.TYPE_INT_RGB);
       iWithoutTurtle = new BufferedImage(maxWidth, maxHeight,BufferedImage.TYPE_INT_RGB);
       gWithTurtle = iWithTurtle.getGraphics();
       gWithoutTurtle = iWithoutTurtle.getGraphics();
       gWithoutTurtle.setColor(Color.white);
       gWithoutTurtle.fillRect(0, 0, maxWidth, maxHeight);
       gWithoutTurtle.setColor(Color.black);
       gWithTurtle.setColor(Color.white);
       gWithTurtle.fillRect(0, 0, maxWidth, maxHeight);
       gWithTurtle.setColor(Color.black);
    } // end of if
  } // end of try
  catch (Exception e) {
    System.out.println("Error in restoring the window");
    System.out.println("iOhne = null: "+ (iWithoutTurtle == null));
    paintComponents(gr);
  }
}
   
//----------------- private: help functions ------------------------------

private static double arc(double winkel){
  return winkel*Math.PI/180;
}

private static double degree(double arc){
  return arc*180/Math.PI;
}

private int convertColor(int rgb){
  for(int k = 0; k < 9; k++){
    if (rgbColor[k]==rgb) return k;
  }
  return -1;
}
//----------------- export --------------------------

/**
* the turtle moves forward in the current direction 
* @ steps   number of pixelunits
*/
public synchronized void move(double steps){
  testVisibility();
  try {
    //if(g==null || gWithoutTurtle==null) initGraphics();
    double nextX = Math.round(x + Math.cos(arc(direction))*steps);   //Änderung AAK!! Math.round hinzugefügt
    double nextY = Math.round(y + Math.sin(arc(direction))*steps);   //Änderung AAK!! Math.round hinzugefügt
    g.setColor(tColor);
    gWithoutTurtle.setColor(tColor);
    if (down){
       g.drawLine((int) x, (int) y, (int) nextX, (int) nextY);
       gWithoutTurtle.drawLine((int) x, (int) y, (int) nextX, (int) nextY);
    }
    lastX= (int)x; lastY=(int)y;
    x = nextX; y = nextY;
    restoreView();
  } catch (Exception ex){
      System.out.println("error in moving!");
      System.out.println("gWithoutTurtle == null: "+(gWithoutTurtle==null));
      System.out.println("Frame is Visible: "+jFrame.isVisible());
      System.exit(0);
    }
}

/**
* no drawing while moving, because the pen is up
*/
public void penUp(){
  down=false;
}

/**
* puts the pen down. The turtle draws a line, if it is moving.
*/
public void penDown(){
  down=true;
}

/**
* changes the direction of movement. 
* @param angle     -360 <= angle <= 360, 0 = right, 180 = left
*/
public void setDirection(double angle){
  direction = angle;
  restoreView();
}

/**
* @return  the way our turtle faces; -360 <= direction <= 360
*/
public double getDirection(){return direction;}

/**
* @return  the x-position of the turtle
*/
public double getXPos(){return x;}

/**
 * @return  the width of the window
 */
public double getYPos(){return y;}

/**
* @return   the x-position of the turtle
*/
public double getMaxX() {return container.getBounds().width;}

/**
* @return   the height of the window
*/
public double getMaxY() {return container.getBounds().height;}

/**
* Rotation
* @param angle    -350 <= angle <= 360
*/
public synchronized void turnRight(double angle){
  direction += angle;
  restoreView();
}

/**
* Rotation
* @param angle    -350 <= angle <= 360
*/
public synchronized void turn(double angle){
  direction += angle;
  restoreView();
}

/**
* color of the pen (and of the turtle)
* @param c   colors: black=0, green=1, blue=2, yellow=3, red=4, gray=5, lightGrau=6, white=8
*/
public synchronized void setColor(int c){
  tColor = colors[c];
  colorValue=c;
  restoreView();
}

/**
* get the pencolor
* @return black=0, green=1, blue=2, yellow=3, red=4, gray=5, lightGrau=6, white=8
*/
public int getColor(){return colorValue;}

/**
* facing to the point (x,y)
* @param x  x-value
* @param y  y-value
*/
public synchronized void turnTo(double x, double y){
  setDirection(x-getXPos(), y-getYPos());
  restoreView();
}

/**
 * Write a string, beginning at the current Position (horizontal).
 * The turtle move back to the begin of the sting
 * @param s      a string
 */
public synchronized void write(String s){
  testVisibility();
  gWithoutTurtle.setColor(tColor);
  gWithoutTurtle.setFont(new Font ("Helvetica", Font.BOLD, 14));
  gWithoutTurtle.drawString(s,(int)x,(int)y);
  g.setColor(tColor);
  g.setFont(new Font ("Helvetica", Font.BOLD, 14));
  g.drawString(s,(int)x,(int)y);
  restoreView();
}

/**
 * @param s    0 <= s <= 10
 */
public void setSpeed(int s){
  if (s>10) s=10;
  if (s<0) s= 0;
  delay = 10-s;
}

/**
 * hide the turtle 
 */
public void hideTurtle(){
  isVisible = false;
  if(g==null) initGraphics();
}

/**
 * show the turtle, the turtle is visible as a triangle
 */
public void showTurtle(){
  isVisible = true;
  activ=true;
  if(g==null) initGraphics();
}

//----------------- useful methods ------------------------
/**
* fixing a starting point
* @param sx  x-coordinate
* @param sy  y-coordinate
*/
public synchronized void toStartingPoint(double sx, double sy){
  if (!jFrame.isVisible()){jFrame.setVisible(true);}
  x=sx; y=sy;  startX=x; startY=y;
  lastX= (int)x; lastY=(int)y;
  initGraphics();
  restoreView();
}

/**
 * move to a new position (shortest way)
 * @param nextX  x-coordinate of the new position
 * @param nextY  y-coordinate of the new position
 */
public synchronized void moveTo(double nextX, double nextY){
  testVisibility();
  try{
   g.setColor(tColor);
   gWithoutTurtle.setColor(tColor);
   if (down) {
    g.drawLine((int) x, (int) y, (int) nextX, (int) nextY);
    gWithoutTurtle.drawLine((int) x, (int) y, (int) nextX, (int) nextY);
   }
   lastX= (int)x; lastY=(int)y;
   x = nextX; y = nextY;
   restoreView();
  } catch (Exception e){System.out.println ("Error moving to!");} 
}

/**
 * Move along the outline of a circular arc
 * @param arcAngle      -360 <= angle <= 360
 * @param radius        radius of the circle
 */
public void moveArc(double arcAngle, double radius){
  double preDirection = direction;
  double step = (Math.PI * radius) / 180.0;
  double alpha = arcAngle / Math.abs(arcAngle);
  double rotation = alpha;
  while (Math.abs(rotation) <= Math.abs(arcAngle)){
   turn(alpha);
   rotation += alpha;
   move(step);
  }
  direction = preDirection + arcAngle;
}

/**
 * sets the direction as gradient with deltaX and deltaY
 */
public synchronized void setDirection(double deltaX, double deltaY){
  if (deltaX == 0 && deltaY == 0) direction = 0;
  else if (deltaX == 0 && deltaY < 0) direction = -90;
  else if (deltaX == 0 && deltaY > 0) direction = 90;
  else if (deltaX > 0 && deltaY == 0) direction = 0;
  else if (deltaX < 0 && deltaY == 0) direction = 180;
  else {
    double w = degree(Math.atan((deltaY)/(deltaX)));
    if (deltaX > 0)
     direction = w;
    else direction = 180+w;
  }
  restoreView();
}

/**
* Draw a circle. The position if the turtle does not change.
* @param radius   radius of the circle
*/
public void drawCircle(double radius){
  testVisibility();
  if (!jFrame.isVisible()){jFrame.setVisible(true);}
  gWithoutTurtle.setColor(tColor);
  gWithoutTurtle.drawOval((int)(x-radius),(int)(y-radius),(int)(radius*2),(int)(radius*2));
  g.setColor(tColor);
  g.drawOval((int)(x-radius),(int)(y-radius),(int)(radius*2),(int)(radius*2));
  activ = true;
}

/**
* Fills a circle with the current color. No changing of the position.
* @param radius   radius of the circle.
*/
public void fillCircle(double radius){
  testVisibility();
  gWithoutTurtle.setColor(tColor);
  int x1 = (int)(x-radius);
  int y1 = (int)(y-radius);
  int d =  (int)(radius*2);
  gWithoutTurtle.fillOval(x1,y1,d,d);
  gWithoutTurtle.setClip(x1,y1,d,d);
  g.setClip(gWithoutTurtle.getClip());
  g.drawImage(iWithoutTurtle,0,0,container);
  gWithoutTurtle.setClip(0,0,currentWidth,currentHeight);
  g.setClip(gWithoutTurtle.getClip());
  activ=true;
}

/**
 *Plots a pixel at the current position in the current color
 */
public void plotPixel(){
  //testVisibility();
  int x1 = (int)(x);
  int y1 = (int)(y);
  gWithoutTurtle.setColor(tColor);
  gWithoutTurtle.fillRect(x1,y1,1,1);
  g.setColor(tColor);
  g.fillRect(x1,y1,1,1);
  activ=true;
}
  
/**
*@param time  in milliseconds
*/
public void sleep(int time){
  try {
   Thread.sleep(time);
  } catch (InterruptedException ie){}
}
   
/**
 * Copy of a pixel. The new position of the turtle is P(xTarget, yTarget)
 * Die position of the turtle will be later xTarget, xTarget
 * @param x           x-coordinate of the source
 * @param y           y-coordinate of the source
 * @param xTarget     x-coordinate of the target
 * @param yTarget     y-coordinate of the target
*/
public void copyPixel(int x, int y, int xTarget, int yTarget){
  boolean status = down;
  down = false;
  if(isVisible) moveTo(x,y);
  gWithoutTurtle.copyArea(x, y, 1, 1, xTarget-x, yTarget-y);
  gWithoutTurtle.setClip(xTarget,yTarget,1,1);
  g.setClip(gWithoutTurtle.getClip());
  g.drawImage(iWithoutTurtle,0,0,container);
  g.setClip(0,0,currentWidth,currentHeight);
  gWithoutTurtle.setClip(0,0,currentWidth,currentHeight);
  if (isVisible) moveTo(xTarget, yTarget); else {x=xTarget; y=yTarget;}
  down = status;
}

/**
* @return color of the current position as int (0 <= color <= 8)
*/
public int getPixelColor(){
  WritableRaster raster = iWithoutTurtle.getRaster();
  DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();
  int[] values = buffer.getData();
  int c = values[(int)(y*iWithoutTurtle.getWidth() + x)];
  int pixelColor = convertColor(c);
  return pixelColor;
}
//Änderung AAK!!
//---------------- events -----------------------  #6#
//    static final Vector listeners=new Vector();
//    
//    public static void register(EventStation er){
//        listeners.add(er);       
//    }
//    
//    public void mousePressed(MouseEvent me){sendEvent(me);}
//    public void mouseReleased(MouseEvent me){sendEvent(me);}
//    public void mouseEntered(MouseEvent me){sendEvent(me);}
//    public void mouseExited(MouseEvent me){sendEvent(me);}
//    public void mouseClicked(MouseEvent me){sendEvent(me);}
//    
//    public void mouseMoved(MouseEvent me){sendEvent(me);}
//    public void mouseDragged(MouseEvent me){sendEvent(me);}
//    
//    private void sendEvent(MouseEvent me){
//        for(int k=0; k<listeners.size(); k++){
//        EventStation er = (EventStation) listeners.elementAt(k);
//        er.handleEvent(me);
//      }//for 
//    }
//----------------german ------------------------
//Änderung AAK!! Einige Methoden umbenannt!
   public static final int schwarz=0, grün=1, gruen=1,blau=2, gelb=3, rot=4, grau=5, hellgrau=6,weiß=8, weiss=8;
   public synchronized void  gehe(double l){move(l);}
   public void hebeStift(){penUp();}
   public void senkeStift(){penDown ();}
   public void setzeRichtung (double degree){setDirection(degree);}
   public synchronized void setzeRichtung(double deltaX, double deltaY){setDirection(deltaX,deltaY);}
   public double liesRichtung(){return getDirection();}
   public double liesX(){return getXPos();}
   public double liesY(){return getYPos();}
   public double liesMaxX(){return getMaxX();}
   public double liesMaxY(){return getMaxY();}
   public synchronized void drehe(double degree){turn(degree);}
   public synchronized void dreheRechts(double degree){turnRight(degree);   }
   public synchronized void setzeFarbe(int c){setColor(c);}
   public int liesFarbe(){return getColor();}
   public synchronized void dreheNach(double x, double y){turnTo(x, y);}
   public synchronized void schreibe(String s){write(s);}
   public void setzeTempo(int s){setSpeed(s);}
   public void verstecke(){hideTurtle();}
   public void zeige(){showTurtle();}
   public synchronized void setzeAufStartpunkt(double sx, double sy){toStartingPoint(sx, sy);}
   public synchronized void geheZu(double newX,double newY){moveTo(newX, newY);}
   public void geheImBogen(double arc, double r){moveArc(arc, r);}
   public void zeichneKreis(double r){drawCircle(r);}
   public void fuelleKreis(double r){fillCircle(r);}
   public void ruhe(int time){sleep(time);}
   public int liesPixelFarbe(){return getPixelColor();}
}
