/* Credits: 

   Books:

   Herbert Shieldt. 
   "The complete reference. Java 2."
   Fifth edition. Page 825.

   Kenneth Litwak
   "Pure Java 2"
   SAMS 2000
   www.samspublishing.com

*/


import java.awt.*;
import java.awt.image.PixelGrabber;
import java.awt.image.MemoryImageSource; 
import java.applet.*;

import Globals.*;
import Approximations.*;

/* TODO
      Questions marked as do1 and do2
      Make GUI to enter parameters in one place
*/


public class Editor extends Applet implements Runnable{

   //Control parameters:
   static final int TRANSPARENT_COLOR = 0;

   //"Yes" means ... dimensions of both images
   //are equal, and they are coisided:
   boolean spriteFitsBase;

   int animationFramesNumber;
   int rotationCenterOnBaseX;
   int rotationCenterOnBaseY;
   int leftTopOfSpriteOnBaseX;
   int leftTopOfSpriteOnBaseY;

   Image baseIm;
   Image spriteIm;
   Image topIm;


   //Auxiliary:

   MediaTracker mt;

   Graphics baseG;
   Graphics spriteG;
   PixelGrabber spritePG;
   PixelGrabber basePG; 
   int[] spriteArr;
   int[] baseArr;
   int[] baseArrBuff;
   Image baseWithSprite;

   Image resultIm;
   Graphics resultG;


   int columnNumber;
   int rowNumber;
   int resultX;
   int resultY;

   int baseW;
   int baseH;
   int spriteW;
   int spriteH;

   //Auxiliary members:
   private static boolean imageIsReady;
   private static       int frameNumber = 0;

   private static    Thread calculateThread;
   private static final long frameDelay = 20;
   private static       long timeToPrintNewFrame;
   private static       long timeLeftAfterCalculations;

   Dimension scrDimension;




   public void init() {


         //Control parameters:
         spriteFitsBase = true;
         columnNumber = 4;
         rowNumber    = 4;
         animationFramesNumber = columnNumber*rowNumber - 2;
         resultX      = -1; //Flag.
         scrDimension = getSize();
   
         imageIsReady = false;
         frameNumber = 0;


         //Principals:
         mt = new MediaTracker(this);

          
         baseIm   = getImage( getCodeBase(), "Resources/base.gif" );
         spriteIm = getImage( getCodeBase(), "Resources/sprite.gif" );
         topIm    = getImage( getCodeBase(), "Resources/top.gif" );

         mt.addImage( baseIm,   0 );
         mt.addImage( spriteIm, 1 );
         mt.addImage( topIm,    2 );

         //do2 Why mt does not load without these?:
         mt.statusID(0, true); 
         mt.statusID(1, true); 
         mt.statusID(2, true); 

         //do1 infinite loops:
         try {
             while( !mt.checkAll()  ) {
                    GS.con("Still downloading images ... " + 
                           "base="    + trackerStatus(mt,0) + 
                           " sprite=" + trackerStatus(mt,1)   );
                    Thread.sleep(1000);
             }

             while( baseIm.getWidth(this)  <=0   || 
                    baseIm.getHeight(this) <=0   ||
                    spriteIm.getWidth(this)  <=0 || 
                    spriteIm.getHeight(this) <=0  ) {
                    GS.con("Still negative dimensions ... "); 
                    Thread.sleep(1000);
             }

             baseW=baseIm.getWidth(this);
             baseH=baseIm.getHeight(this);
             spriteW=baseIm.getWidth(this);
             spriteH=baseIm.getHeight(this);

             //Do control:
             rotationCenterOnBaseX =  baseW/2;
             rotationCenterOnBaseY =  baseH/2;
             leftTopOfSpriteOnBaseX = baseW/5;
             leftTopOfSpriteOnBaseY = baseH/5;
             if( spriteFitsBase ) {
                 leftTopOfSpriteOnBaseX = 0;
                 leftTopOfSpriteOnBaseY = 0;
             }

         } catch (Exception e) {
             e.printStackTrace();
         }
         if( mt.isErrorAny() ) {
             GS.con("Problems with some images ... ");
             //int st = mt.statusID(0, false); 
         } else {
           GS.con( "All images downloaded successfully." );

           //do2: Strangerly: these calls "shakes" dimensions and
           //enables them later:
           //GS.con( "Width=" + baseIm.getWidth(this) + 
           //        " Height=" + baseIm.getHeight(this)   );

           resultX = -2; //Success.
         }   

         ICos.generate();
         GS.con("Init finished");       


   } //init()


   private String trackerStatus( MediaTracker mt, int index ) {
           String s = "";
           int st = mt.statusID(index, false); 
           if(  0 == st ) { s = "NOT STARTED"; return s; }
           if(  (st & MediaTracker.ABORTED)  !=0  )  s = s + "ABORTED ";
           if(  (st & MediaTracker.COMPLETE) !=0  )  s = s + "COMPLETE ";
           if(  (st & MediaTracker.ERRORED)  !=0  )  s = s + "ERRORED ";
           if(  (st & MediaTracker.LOADING)  !=0  )  s = s + "LOADING ";
           return s;
   }

   public void start() {
      timeToPrintNewFrame = System.currentTimeMillis();
      if( calculateThread == null ) {
          calculateThread = new Thread(this);
          calculateThread.setName("Calc");
          calculateThread.start();
          GS.con( calculateThread.getName() + " initiated." );
      }
   }



   public void run() {

     try {
       while( Thread.currentThread() == calculateThread ) {
          timeLeftAfterCalculations = timeToPrintNewFrame - System.currentTimeMillis();
          repaint();
          Thread.sleep(Math.max(0, timeLeftAfterCalculations));
          frameNumber++;
          //GS.con( "Finished sleeping..." );
          timeToPrintNewFrame = System.currentTimeMillis() + frameDelay;

          if( resultX == -2 ) {
                    resultX      = columnNumber * baseW;
                    resultY      = columnNumber * baseH;
                    GS.con ("w=" + resultX + " h=" + resultY);
                    resultIm = createImage(resultX,resultY);
                    resultG  = resultIm.getGraphics();
                    GS.con("Result image graphics created.");

                    spriteArr   = new int[spriteW*spriteH];
                    baseArr     = new int[baseW*baseH];
                    baseArrBuff = new int[baseW*baseH];

                    spritePG  = new PixelGrabber(spriteIm, 0, 0, spriteW, spriteH, spriteArr, 0, spriteW);
                    basePG    = new PixelGrabber(baseIm, 0, 0, baseW,   baseH,   baseArr,   0, baseW  );

                    /*
                    PixelGrabber(Image img,
                    int x,
                    int y,
                    int w,
                    int h,
                    int[] pix,
                    int off,
                    int scansize)
                    */

                    try {
                          spritePG.grabPixels();
                          basePG.grabPixels();
                    } catch (InterruptedException e) {
                      GS.con("Faile grab pixels: " + e);
                      this.stop();
                    }
                    GS.con("Pixels grabbed successfully ... ");

                    calculateImage();
                    
                      this.stop();

                    //Debug:
                    //this.stop();
          }

       }
     } catch (InterruptedException e) {
             GS.con( "In method run" + e );
     }

   } //run()


   public void update( Graphics g ) {
     if( imageIsReady && resultIm != null ) {

       //Move result image to master image:
       g.drawImage( resultIm, 0,0, null );

       g.drawImage( spriteIm, resultX, 0, null );
       g.drawImage( baseWithSprite, resultX, baseH, null );

       int wi = frameNumber % animationFramesNumber;
       int animationI =  wi % columnNumber;
       int animationJ = (wi - animationI )/columnNumber;

       //Put animated image last:
       g.drawImage( resultIm, //=Source 
                              //Target:
                    resultX, 
                    baseH*2,
                    resultX+baseW-1,
                    baseH*3      -1,
                              //Source:
                    animationI*baseW, 
                    animationJ*baseH, 
                    animationI*baseW+baseW-1,
                    animationJ*baseH+baseH-1,

                    null      //=ImageObserver
                  );

       //Debug: 
       //g.drawString( "Frame=" + frameNumber +
       //              "wi=" + wi + " i=" + animationI +
       //              " j=" + animationJ, 0, resultY/2 );
                     
       //Debug:
       //calculateThread.stop(); 

     } else {

       g.setColor( new Color(0) );
       g.drawString( "Image is not ready yet", 0,0 );

     }
     
     paint(g);
   }



   void calculateImage() {

        //Debug
        resultG.setColor(new Color( (255<<16) | (155<<8) | 255 ) );
        resultG.fillRect(0,0,resultX-1,resultY-1);
        resultG.setColor( new Color( 0, 0, 0 ) );
        resultG.drawString("Image Created", 3, 3 );

        for( int i=0; i<columnNumber; i++ ) {
             for( int j=0; j<rowNumber; j++ ) {
                  resultG.drawImage( baseIm, i*baseW, j*baseH, null );
                  //if( 1 == i && 1 == j ) {

                      drawRotatedSprite( 
                           rotationCenterOnBaseX,
                           rotationCenterOnBaseY,
                           leftTopOfSpriteOnBaseX,
                           leftTopOfSpriteOnBaseY,
                           ICos.argMax4/3,
                           animationFramesNumber,
                           j*columnNumber + i
                         );
                      resultG.drawImage( baseWithSprite, i*baseW, j*baseH, null );
                //}   
            }
        }

        //Debug for cos, sin: 
        //ICos.drawTest( resultG, resultY );

        GS.con( "Mutiframe image created." );

        imageIsReady = true;

   }

   public void drawRotatedSprite( int circleCenterX,
                                  int circleCenterY,
                                  int spritePosX,
                                  int spritePosY,
                                  int startAngle,
                                  int framesNumber,
                                  int currentFrame
                                ) {

               int argI = (ICos.argMax4*currentFrame)/framesNumber;
               if(argI>ICos.argMax4) argI = ICos.argMax4;
               int cosI = ICos.cosI[argI];
               int sinI = ICos.sinI[argI];
               int sinMax = ICos.sinMax;

               int spRelPosX =  spritePosX - circleCenterX;
               int spRelPosY = -(spritePosY- circleCenterY); //Normal, not screen coord.

               //GS.b("Copying to buffer..");
               for(int i=0; i<baseW; i++ ) {
                   //do1 use copyMem
                   for(int j=0; j<baseH; j++ ) {
                       int ix = j*baseW + i;
                       baseArrBuff[ix] = baseArr[ix];
                   }   
               }
               
               //GS.m("rotating..");
               for(int i=0; i<spriteW; i++) {
                   int fullX = i + spRelPosX;
                   for(int j=0; j<spriteH; j++ ) {
                       int color = spriteArr[j*baseW + i];
                       //if( (color & 0xFFFFFF ) != TRANSPARENT_COLOR ) {
                           int fullY = spRelPosY-j;
                           int xS = (fullX*cosI + fullY*sinI)/sinMax + circleCenterX;
                           int yS = circleCenterY -((-fullX*sinI) + fullY*cosI)/sinMax;
                           if( xS < baseW && xS > -1 && 
                               yS < baseH && yS > -1 ) {
                               int ix = yS*baseW + xS;
                               baseArrBuff[ix] = color;
                           }
                       //}
                    }
               } 
               //GS.e("rotated.");
               baseWithSprite= createImage( new MemoryImageSource( 
                                            baseW, baseH, baseArrBuff, 0, baseW));
               //GS.con("Created from arr to graph. ... ");

   } //drawRotatedSprite()
                                   


/* Credits: 

   Books:

   Herbert Shieldt. 
   "The complete reference. Java 2."
   Fifth edition. Page 825.

   Kenneth Litwak
   "Pure Java 2"
   SAMS 2000
   www.samspublishing.com

*/


} // class