// *********************************************************
// * Turn.class                                            *
// *********************************************************
// *********************************************************
// * Beschreibung:                                         *
// * Ein beliebiges Bild (JPG oder GIF (auch transparent)) *
// * wird vor einem Hintergrund um die Y-Achse rotiert     *
// * wird. Der Hintergrund wird entweder mit Farbe         *
// * oder einem Texturbild (das nicht genau den Massen des *
// * Applets entsprechen muss) ausgefuellt.                *
// * Alle Animationsphasen werden im Voraus berechnet,     *
// * das Applet hinreichend schnell ablaeuft.              *
// * Bevor man das Applet in eine Seite einbindet, kann    *
// * man das Offset der Textur (nur auf Wunsch) mit den    *
// * Cursortasten steuern, damit sich die Textur genau mit *
// * einer eventuellen Textur der eigentlichen HTML-Seite  *
// * deckt.                                                *
// *********************************************************
// *********************************************************
// * Autor: Sascha Schneider                               *
// * email: st000991@hrz1.hrz.th-darmstadt.de              *
// * Homepage: http://www.th-darmstadt.de/~st000991        *
// *********************************************************
// ************************************************************
// * Parameter:                                               *
// * offsetcheck (optional)    - wenn dieser Parameter auf    *
// *                             "true" gesetzt wird kann     *
// *                             man die optimale Position    *
// *                             seiner Applet-Hintergrund-   *
// *                             Textur ermittlen             *
// * speed (optional)          - Die Pause zwischen den ein-  *
// *                             zelnen Animationsphasen in   *
// *                             ms.                          *
// * bgimage (optional)        - Das Bild, mit dem der        *
// *                             Hintergrund des Applets      *
// *                             ausgefuellt wird (Textur)    *
// * bgcolor (optional)        - Die Farbe, mit der der       *
// *                             Hintergrund ausgefuellt      *
// *                             werden soll (anstatt Textur) *
// * turnimage                 - Das Bild, das gedreht werden *
// *                             soll.                        *
// * xoffset (optional)        - das Xoffset des Hintergrund- *
// *                             Bildes (negativ!).           *
// * yoffset (optional)        - das Yoffset des Hintergrund- *
// *                             Bildes (negativ!).           *    
// * einzelschritte (optional) - Die Anzahl der einzelnen     *
// *                             Animationsphasen (wird aus   *
// *                             Java-Speicherverwaltungs-    *
// *                             gruenden) automatisch auf 75 *
// *                             beschraenkt.                 *
// ************************************************************
// ************************************************************
// * Fragen und Anregungen bitte per email an mich schicken   *
// * st000991@hrz1.hrz.th-darmstadt.de (Sascha Schneider)     *
// ************************************************************
// ************************************************************
// * Description:                                           *
// * A picture (JPG or GIF (even transparent)) is rotatet   *
// * along its y-axis.    The background of the applet is   *
// * either filled up with a specified image (which doesn´t *
// * need to fit exactly into the applet window) or with    *
// * a selected Color. All phases of the animation are      *
// * calculated before the Applet starts to maximize        *
// * animation speed. Before putting the applet on a        *
// * special place on the html-page, you case choose the    *
// * x- and y-offset of the backgroundimage (if specified)  *
// * to get the backgroundimage fit with an texture         *
// * putted on the applet-including html-page.              *
// **********************************************************
// **********************************************************
// * Author: Sascha Schneider                               *
// *  email: st000991@hrz1.hrz.th-darmstadt.de              *
// * Homepage: http://www.th-darmstadt.de/~st000991         *
// **********************************************************
// *************************************************************
// * Parameters:                                               *
// * offsetcheck (optional)    - if this parameter is set      *
// *                             to "true", you can change     *
// *                             the position of the back-     *
// *                              groundimage to get the       *
// *                             optimal x- and yoffset        *
// * speed (optional)          - the pause between the         *
// *                             particual phases of animation *
// *                             in ms                         *
// * bgimage (optional)        - the picture for the           *
// *                             backgroundtexture             *
// * bgcolor (optional)        - the desired color for the     *
// *                             background                    *
// * turnimage                 - the picture which will be     *
// *                             rotated                       *
// * xoffset (optional)        - the xoffset of the background *
// *                             picture                       *
// * yoffset (optional)        - the yoffset of the background *
// *                             picture                       *
// * einzelschritte (optional) - the numbers of steps for the  *
// *                             animation to form a half turn *
// *                             this parameter will be        *
// *                             limited to 75 (for java       *
// *                             memory-management reasons)    *
// *************************************************************    
// *************************************************************
// * Pleas give questions and comments via email to            *
// * st000991@hrz1.hrz.th-darmstadt.de (Sascha Schneider)      *
// *************************************************************
// *************************************************************

import java.awt.*;
import java.awt.image.*;
import java.applet.*;
import java.lang.*;

public class Turn extends Applet implements Runnable {
  boolean offsetcheck;
  Image bufferbild=null;
  Image bgimage=null;
  int bgw, bgh;
  int turnw, turnh;
  int w,h;
  int xoffset;
  int yoffset;
  int xmiddle, ymiddle;
  int speed;
  Thread t=null;
  int[] tpixelarray = null;
  Image bg=null;
  int einzelschritte;
  boolean bgimageangegeben = false;
  boolean bgimagepasst = false;
  Color bgcolor=null;
  int bgcolorint;
  int[][] rpixelarray = null;
  int counter=0;
  
  public void init () {
    Dimension d=size();
    w=d.width;
    h=d.height;
    Image turnimage=null;
    try {this.offsetcheck = (new Boolean(getParameter("offsetcheck"))).booleanValue();}
      catch (NumberFormatException e) { this.offsetcheck = false; } // falls Typumwandlung scheitert setze offsetcheck auf false
    try {this.xoffset = (new Integer(getParameter("xoffset"))).intValue();}
      catch (NumberFormatException e) { this.xoffset = 0; } // falls Typumwandlung scheitert setze xoffset auf 0
    try {this.yoffset = (new Integer(getParameter("yoffset"))).intValue();}
      catch (NumberFormatException e) { this.yoffset = 0; } // falls Typumwandlung scheitert setze yoffset auf 0
    try {this.einzelschritte = (new Integer(getParameter("einzelschritte"))).intValue();}
      catch (NumberFormatException e) { this.einzelschritte = 10; } // falls Typumwandlung scheitert setze einzelschritte auf 10
    if (einzelschritte > 75) einzelschritte = 75;
    try {this.speed = (new Integer(getParameter("speed"))).intValue();}
      catch (NumberFormatException e) { this.speed = 250; } // falls Typumwandlung scheitert setze speed auf 250
    try {this.bgcolor = new Color(Integer.valueOf(getParameter("bgcolor"),16).intValue());}
      catch (NumberFormatException e) { bgcolor = Color.green; } // falls Typumwandlung scheitert setze speed auf gruen
    this.bgcolorint = (255 << 24) | (bgcolor.getRed() << 16) | (bgcolor.getGreen() << 8) | bgcolor.getBlue();
    if (getParameter("bgimage") != null) bgimageangegeben = true;
    int[] bgpixelarray;
    try {
      if (bgimageangegeben) { bgimage=getImage(getDocumentBase(),getParameter("bgimage"));}
      else { bgimage=createImage(w,h); };
      turnimage=getImage(getDocumentBase(),getParameter("turnimage"));
      MediaTracker m = new MediaTracker(this);
      m.addImage(bgimage,0);
      m.addImage(turnimage,1);
      m.waitForID(0);
      m.waitForID(1);
      bgw=bgimage.getWidth(this);
      bgh=bgimage.getHeight(this);
      turnw=turnimage.getWidth(this);
      turnh=turnimage.getHeight(this);
      xoffset%=bgw; // xoffsets in erlaubten Bereich holen
      yoffset%=bgh;
      if (xoffset > 0) xoffset-=bgw;
      if (yoffset > 0) yoffset-=bgh;
      tpixelarray = new int[turnw*turnh];
      PixelGrabber pg = new PixelGrabber(turnimage,0,0,turnw,turnh,tpixelarray,0,turnw);
      pg.grabPixels();
      bgpixelarray = new int[bgw*bgh];                                             
      pg = new PixelGrabber(bgimage,0,0,bgw,bgh,bgpixelarray,0,bgw);
      pg.grabPixels();
    if (!offsetcheck) {
      int[] completebgpixelarray = new int [w*h];
      int i,j,k,l;
      if (bgimageangegeben == false) { // falls kein Hintergrundbild angegeben, fuelle mit Hintergrundfarbe
        for (i=0; (i < w*h); i += 1) { completebgpixelarray[i]=bgcolorint; }
        bg=createImage(new MemoryImageSource(w,h,completebgpixelarray,0,w));
      }
      else {
      // mache mir das hintergrundbild
      if (h >= bgh) {
        //0000 wenns mehr als eine gibt!
        //xxxx 1. Zeile
          if (w >= bgw) {
            bgimagepasst = true;
            //xxxx wenns mehr als eine Spalte gibt
            // mache das erste bild in der ersten zeile (2fach abgeschnitten 1x,1y)
            for (i=-yoffset; (i < bgh); i += 1) {
              for (j=-xoffset; (j < bgw); j += 1) {
                completebgpixelarray[(i+yoffset)*w+(j+xoffset)]=bgpixelarray[i*bgw+j];
              }
            }
            // mache alle anderen ganzen bilder in der zeile (1fach abgeschnitten - y)
            int xa, ya;
            xa = w - (bgw+xoffset);
            ya = h - (bgh+yoffset);
            xa /= bgw; // restliche anzahl ganze bilder in der Zeile
            ya /= bgh; // restliche anzahl ganze bilder in der Spalte
            for (k = 1; k <= xa; k +=1) {
              for (i = -yoffset; (i < bgh); i +=1) {
                for (j = 0; (j < bgw); j +=1) {
                  completebgpixelarray[(bgw+xoffset)+(k-1)*bgw+w*(i+yoffset)+j]=bgpixelarray[i*bgw+j];
                }
              }
            } // for k
          // mache das letzte bild in der ersten zeile (2fach abgeschnitten 1x,1y)
          for (i=-yoffset; (i < bgh); i += 1) {
            for (j=0; (j < (w-(bgw+xoffset+xa*bgw))); j += 1) {
              completebgpixelarray[(bgw+xoffset+xa*bgw)+w*(i+yoffset)+j]=bgpixelarray[i*bgw+j];
            }
          }
          //xxxx mache mir alle dazwischenliegenden zeilen (gibts die?)
            for (l = 1; l <= ya; l +=1) {
            // 1. bild (1fach abgeschnitten - x)
              for (i=0; (i < bgh); i += 1) {
                for (j=-xoffset; (j < bgw); j += 1) {
                  completebgpixelarray[((l-1)*w*bgh)+(bgh+yoffset)*w+(j+xoffset)+i*w]=bgpixelarray[i*bgw+j];
                }
              }
            // alle anderen (nicht abgeschnitten)
              for (k = 1; k <= xa; k +=1) {
                for (i = 0; (i < bgh); i +=1) {
                  for (j = 0; (j < bgw); j +=1) {
                      completebgpixelarray[((l-1)*w*bgh)+(bgh+yoffset)*w+(bgw+xoffset)+(k-1)*bgw+w*i+j]=bgpixelarray[i*bgw+j];
                  }
                }
              } // for k
            // letztes bild (1fach abgeschnitten - x)
              for (i=0; (i < bgh); i += 1) {
                for (j=0; (j < (w-(bgw+xoffset+xa*bgw))); j += 1) {
                  completebgpixelarray[((l-1)*w*bgh)+(bgh+yoffset)*w+(bgw+xoffset+xa*bgw)+w*i+j]=bgpixelarray[i*bgw+j];
                }
              }
            } // for l
          //xxxx mache die letzte zeile 
            // 1. bild (2fach 1x,1y)
                for (i = 0; (i < (h-(bgh+yoffset+ya*bgh))); i +=1) {
                  for (j = -xoffset; (j < bgw); j +=1) {
                    completebgpixelarray[(bgh+yoffset)*w+(ya*bgh)*w+i*w+(j+xoffset)]=bgpixelarray[i*bgw+j];
                  }
                }
            // alle anderen (1fach y)
                for (k = 1; k <= xa; k +=1) {
                  for (i = 0; (i < (h-(bgh+yoffset+ya*bgh))); i +=1) {
                    for (j = 0; (j < bgw); j +=1) {
                      completebgpixelarray[(bgh+yoffset)*w+(ya*bgh)*w+(bgw+xoffset)+(k-1)*bgw+w*i+j]=bgpixelarray[i*bgw+j];
                    }
                  }
                } // for k
            // letztes bild (2fach 1x,1y)
                for (i = 0; (i < (h-(bgh+yoffset+ya*bgh))); i +=1) {
                  for (j = 0; (j < (w-(bgw+xoffset+xa*bgw))); j +=1) {
                    completebgpixelarray[(bgh+yoffset)*w+(ya*bgh)*w+(xa*bgw)+(bgw+xoffset)+i*w+j]=bgpixelarray[i*bgw+j];
                  }
                }
          }
      }
      bg=createImage(new MemoryImageSource(w,h,completebgpixelarray,0,w));
    } 
    }
    } catch (InterruptedException e) {};
    xmiddle=w/2;
    ymiddle=h/2;
    bufferbild=createImage(w,h);
    if (!offsetcheck) { 
      double phi, phistep;
      phi=0;
      phistep=Math.PI/einzelschritte;
      // *******************************************************
      // * Jetzt werden erst mal alle einzelnen Bilder erzeugt *
      // *******************************************************
      rpixelarray= new int[einzelschritte][turnw*turnh];
      int a, b, c;
      double m;
      for    (c = 0; c < einzelschritte; c +=1) {
        m = Math.sin(phi);
        showStatus("Turn Applet: Berechne Frame "+c+"/"+einzelschritte);
        for (a=0; (a < turnw*turnh); a += 1) { rpixelarray[c][a]=(0 << 24) | (0 << 16) | (0 << 8) | 0; }; // erst mal alles transparent ausfuellen
        for (a = 0;(a < turnh); a += 1) {
          for (b = turnw/2;(b >= 0); b -=1) {
            rpixelarray[c][(int)(Math.abs(a*turnw+turnw/2-(m*b)))]=tpixelarray[a*turnw+turnw/2-b];
          }
        }
        for (a = 0;(a < turnh); a += 1) {
          for (b = 0;(b < turnw/2); b +=1) {
            rpixelarray[c][(int)(Math.abs(a*turnw+turnw/2+(m*b)))]=tpixelarray[a*turnw+turnw/2+b];
          }
        }
        phi+=phistep;
        if (phi > (Math.PI)) phi %= Math.PI;
      }
      showStatus("");
      counter=0;
      t = new Thread(this);
      t.start();
      t.suspend();
    }
  }

  public void start() {
    if (offsetcheck == false) {
      t.resume();
    }
  }

  public void run() {
    if (offsetcheck == false) {
      for ( ; ; ) {
          try {
          repaint();
          Thread.sleep(speed);
          counter+=1;
          if (counter >= 2*einzelschritte-1) counter=0;
        } catch (InterruptedException e) {}
      }
    }
  }

  public void stop () {
    if (offsetcheck == false) {
      t.suspend();
    }
  }

  public void destroy () {
    if (offsetcheck == false) {
      t.stop();
      t = null;
    }
  }

  public void paint (Graphics g) {
    //if (offsetcheck == false) t.suspend();
    Graphics screenretter = null;
    screenretter = g; // Zeiger auf ScreenContext retten
    g=bufferbild.getGraphics(); // male ab jetzt im bufferbild
    if (offsetcheck) {
      g.setColor(Color.white);
      g.fillRect(0,0,w,h);
      g.drawImage(bgimage,xoffset,yoffset,this);
      g.setColor(Color.black);
      g.drawString("xoffset:"+xoffset,10,h/2);
      g.drawString("yoffset:"+yoffset,10,h/2+10);
      g.drawString("offsetcheck:"+offsetcheck,10,h/2+20);
      g.drawString("bgw,bgh:"+bgw+","+bgh,10,h/2+30);
      showStatus("Turn Applet: Offsetcheck aktiviert. Steuere Textur mit Cursortasten.");
    }
    else {
      if ((bgimageangegeben) & (bgimagepasst == false)) { // falls Hintergrundbild zu gross war, muss es eben von Hand gemacht werden
        int a, b;
        for (a=0; a <= 1; a+=1) {
          for (b=0; b <= 1; b+=1) {
            g.drawImage(bgimage,xoffset+a*bgw,yoffset+b*bgh,this);
          }
        }
      } else {
        g.drawImage(bg,0,0,this); // male das fertige hintergrundbild in den ScreenContext
        }
      int helpimagearray[] = new int[turnw*turnh];
      if (counter < einzelschritte)
        for (int s=0; s < (turnw*turnh); s += 1)
          helpimagearray[s] = rpixelarray[counter][s];
      else
        for (int s=0; s < turnh; s += 1)
          for (int u=0; u < turnw; u += 1)
            helpimagearray[s*turnw+u] = rpixelarray[counter-einzelschritte][s*turnw+turnw-u-1];
      Image rimage = createImage(new MemoryImageSource(turnw,turnh,helpimagearray,0,turnw));
      g.drawImage(rimage,xmiddle-turnw/2,ymiddle-turnh/2,this);
    }
    screenretter.drawImage(bufferbild,0,0,this); // male das fertige bufferbild in den ScreenContext
  //if (offsetcheck == false) t.resume();
}

  public void update(Graphics g) {
    paint(g);
  }

  public boolean keyDown(Event evtObj, int key){
    if (offsetcheck) {
      switch(key) {
        case Event.LEFT : xoffset-=1; break;
        case Event.RIGHT: xoffset+=1; break;
        case Event.DOWN : yoffset+=1; break;
        case Event.UP   : yoffset-=1; break;
        case ' '        : xoffset=0; yoffset=0; break;
      }
      xoffset%=bgw;
      yoffset%=bgh;
      if (xoffset > 0) xoffset-=bgw;
      if (yoffset > 0) yoffset-=bgh;
      repaint();
    }
    return true;
  }

  public boolean keyUp(Event evtObj, int key) {
    return true;
  }

}