/*********************************************************************************************/
/*                                                                                           */
/*  Java Image Demo -Fire Pattern                                                            */
/*  Auth: bkenwright@xbdev.net                                                               */
/*  URL: www.xbdev.net                                                                       */
/*                                                                                           */
/*  Fire                                                                                     */
/*                                                                                           */
/*********************************************************************************************/

import java.awt.image.*;
import java.awt.*;
import java.applet.*;

import java.util.Random;

/*colour container*/
class Colour
{
  public int r,g,b;
}

/*particle structure*/
class Particle 
{
  public Particle(){};
  public int xpos;
	public int ypos;
	public int xdir;
	public int ydir;
  public int colorindex;
  public int dead;
}

public class explosion extends Applet implements Runnable
{
   private Thread m_thread        = null;
   public Screen scr;

   public synchronized void update(Graphics g)
   {
      int[] pixels = scr.GetPixels();
      int w=scr.GetWidth();    
      int h=scr.GetHeight();

      Pattern(pixels, w, h );
      scr.Render(g);
   }// End update(..)


   public void init()
   {
      scr = new Screen(320,320);
      initPattern();
   }// End init(..)

   int[] fire;
   Colour[] colours;
   Random generator = new Random();

   int NUMBER_OF_PARTICLES = 500;
   Particle[] particles = new Particle[500];
   int SCREEN_WIDTH = 320;
   int SCREEN_HEIGHT = 320;

   int rand()
   {
      int RAND_MAX = 32767;
      int v = Math.abs(  (int)(generator.nextInt())  );
      v = v % RAND_MAX;
      return v;
   }//End rand

   void init_particle(Particle particle[], int iNumParticles)
   {
      for(int i=0; i<iNumParticles; i++)
      {
         Particle p = particles[i];

         /* randomly init particles, generate them in the center of the screen */
         int RAND_MAX = 32767;

         p.xpos = (SCREEN_WIDTH >> 1) - 20 + (int)(40.0 * (rand()/(RAND_MAX+1.0)));
         p.ypos =  (SCREEN_HEIGHT >> 1) - 20 + (int)(40.0 * (rand()/(RAND_MAX+1.0)));
         p.xdir =   7- (rand() % 14);//-10 + (int)(20.0 * (rand()/(RAND_MAX+1.0)));
         p.ydir =   2 -(rand() % 19);//-17 + (int)(19.0 * (rand()/(RAND_MAX+1.0)));
         p.colorindex = 255;
         p.dead = 0;

      }//End for loop
   }// End init_particle(..)

   void initPattern()
   {
      fire = new int[ scr.GetWidth() * scr.GetHeight() ];
      colours = new Colour[ 256 ];

      /* fill fire array with black*/
      for(int p=0; p< (SCREEN_WIDTH-1)*(SCREEN_HEIGHT-1); p++)
      {
         fire[p] = 0;
      }//End for loop

      
      for(int t=0; t<256; t++)
         colours[t] = new Colour();

      /* create a suitable fire palette, this is crucial for a good effect */
      /* black to blue, blue to red, red to yellow, yellow to white*/
      int i;
      for (i = 0; i < 32; ++i)
      {
         /* black to blue, 32 values*/
         colours[i].b = i << 1;

         /* blue to red, 32 values*/
         colours[i + 32].r = i << 3;
         colours[i + 32].b =  64 - (i << 1);

         /*red to yellow, 32 values*/
         colours[i + 64].r = 255;
         colours[i + 64].g = i << 3;

         /* yellow to white, 162 */
         colours[i + 96].r = 255;
         colours[i + 96].g = 255;
         colours[i + 96].b = i << 2;
         colours[i + 128].r = 255;
         colours[i + 128].g = 255;
         colours[i + 128].b = 64 + (i << 2);
         colours[i + 160].r = 255;
         colours[i + 160].g = 255;
         colours[i + 160].b = 128 + (i << 2);
         colours[i + 192].r = 255;
         colours[i + 192].g = 255;
         colours[i + 192].b = 192 + i;
         colours[i + 224].r = 255;
         colours[i + 224].g = 255;
         colours[i + 224].b = 224 + i;
      }// End for loop
 

      for(int k=0; k<NUMBER_OF_PARTICLES; k++)
      {
         particles[k] = new Particle();
      }

      init_particle(particles, NUMBER_OF_PARTICLES);
      

   }//End initPattern()


   void Pattern(int pix[], int width, int height)
   {
      int buf, index, temp;
      int i, j; 


      /* move and draw particles into fire array */

      for (i = 0; i < NUMBER_OF_PARTICLES; i++)
      {
	  if (particles[i].dead == 0)
	    {
	      particles[i].xpos += particles[i].xdir;
	      particles[i].ypos += particles[i].ydir;
	  
	      /* is particle dead? */
	  
	      if ((particles[i].ypos >= SCREEN_HEIGHT - 3) || particles[i].colorindex == 0 || 
		  particles[i].xpos <= 1 || particles[i].xpos >= SCREEN_WIDTH - 3)
		{
		  particles[i].dead = 1;
		  continue;
		}
               else
              {
	  
	      /* gravity takes over */
	  
	      particles[i].ydir++;
	  
	      /* particle cools off */
	  
	      particles[i].colorindex--;
	  
	      /* draw particle */
	  
	      temp = particles[i].ypos * SCREEN_WIDTH + particles[i].xpos;
	  
	      fire[temp] = particles[i].colorindex;
	      fire[temp - 1] = particles[i].colorindex;
	      fire[temp + SCREEN_WIDTH] = particles[i].colorindex;
	      fire[temp - SCREEN_WIDTH] = particles[i].colorindex;
	      fire[temp + 1] = particles[i].colorindex;
                }
	    }
        }// End for loop


      /* create fire effect */
  
      for (i = 1; i < SCREEN_HEIGHT - 2; i++)
	{
	  index = (i  - 1) * SCREEN_WIDTH;
      
	  for (j = 1; j < SCREEN_WIDTH - 2; j++)
	    {
	      buf = index + j;
	  
	      temp = fire[buf];
	      temp += fire[buf + 1];
	      temp += fire[buf - 1];
	      buf += SCREEN_WIDTH;
	      temp += fire[buf - 1];
	      temp += fire[buf + 1];
	      buf += SCREEN_WIDTH;
	      temp += fire[buf];
	      temp += fire[buf + 1];
	      temp += fire[buf - 1];
	  
	      temp >>= 3;
	  
	      if (temp > 4)
		{
		  temp -= 4;
		}
	      else
		{
		  temp = 0;
		}

              
              if(temp < 0 ) temp = 0;
              if(temp > 255 ) temp = 255;
	      
	      fire[buf - SCREEN_WIDTH] = temp;
	    }
	}

      /* draw fire array to screen from bottom to top + 300*/
      int[] image = pix;
      //System.out.println("width: " + width + "  height:"+ height );
      int cc = (width)*(height-1);
      for (i = height - 3; i >= 0; --i)
	{
	  for (j = width - 1; j >= 0; --j)
	    {
              int indx = fire[i * width + j];
	      int r = colours[indx].r;
	      int g = colours[indx].g;
	      int b = colours[indx].b;
              int col = (0xff<<24)|(r<<16)|(g<<8)|(b);
              image[cc] = col;
	      cc--;
	    }
	}

   }//End Pattern(..)



   void PatternX(int pix[], int width, int height)
   {
      int i, j;
      float temp;
       
      /* draw random bottom line in fire array*/

      j = width * (height - 1);
      for (i = 0; i < width - 1; i++)
      {
         int RAND_MAX = 32767;
         float randv = (float)(generator.nextInt());
         
         int random = 1 + (int)(16.0 * (randv/(RAND_MAX+1.0)));

         if (random > 9) /* the lower the value, the intenser the fire, compensate a lower value with a higher decay value*/
            fire[j + i] = 255; /*maximum heat*/
         else
	    fire[j + i] = 0;
      }// End for loop

      
      /* move fire upwards, start at bottom*/
      
      for (int index = 0; index < 200 ; ++index)
	{
	  for (i = 0; i < width - 1; ++i)
	    {
	      if (i == 0) /* at the left border*/
		{
		  temp = fire[j];
		  temp += fire[j + 1];
		  temp += fire[j - width];
		  temp /=3;
		}
	      else if (i == width - 1) /* at the right border*/
		  {
		    temp = fire[j + i];
		    temp += fire[j - width + i];
		    temp += fire[j + i - 1];
		    temp /= 3;
		  }
	      else
		{
		  temp = fire[j + i];
		  temp += fire[j + i + 1];
		  temp += fire[j + i - 1];
		  temp += fire[j - width + i];
		  //temp >>= 2;
                  temp /= 4;
		}
	      if (temp > 1)
                 temp -= 0.1f;/* decay */
                 
	      	 //temp -= 1; /* decay use 1.0 for a small flame :) */

              if(temp < 0 ) temp = 0;
              if(temp > 255.0f ) temp = 255.0f;

	      fire[j - width + i] = (int)temp;
	    }
	  j -= width;
	}      

      /* draw fire array to screen from bottom to top + 300*/
      int[] image = pix;
      //System.out.println("width: " + width + "  height:"+ height );
      int cc = (width)*(height-1);
      for (i = height - 3; i >= 200; --i)
	{
	  for (j = width - 1; j >= 0; --j)
	    {
              int indx = fire[i * width + j];
	      int r = colours[indx].r;
	      int g = colours[indx].g;
	      int b = colours[indx].b;
              int col = (0xff<<24)|(r<<16)|(g<<8)|(b);
              image[cc] = col;
	      cc--;
	    }
	}

	
   }// End Pattern(..)



	
  
   public void run()
   {
	    
      // Remember the starting time
      long thenTime = System.currentTimeMillis();
      while (true) 
      {
         long nowTime = System.currentTimeMillis();
         long difTime = nowTime - thenTime;
			    
         // I've set the delay to 0, so its pushing for the MAX fps, you would
         // set a value in there to limit for example to 25-50 fps refresh rate
         if( difTime > 10 )
	 {
            thenTime = nowTime;
            // This method provides better updates, instead of filling the
            // message queue up
            Graphics g=this.getGraphics();
            update(g);
	 }
      }// end while loop
   }// End of run() method
	
	
   public void start()
   {
      if(m_thread == null)
      {
         m_thread = new Thread(this);
         //m_thread.setPriority(Thread.MAX_PRIORITY);
         m_thread.start();
      }
      System.out.println( "Starting Explosion Graphics Demo\n" );
   }// End of start()


   public boolean mouseDown(Event evt, int x, int y)
   {
      init_particle(particles, NUMBER_OF_PARTICLES);
      //repaint();
      return true;
   }// End of mouseDown(..)
	
}// End of Applet





/*********************************************************************************************/
/*                                                                                           */
/*  class Screen                                                                             */
/*  Well its the fastest method to plot pixels using MemoryImageSource...and to access your  */
/*  array of pixel data directly...but it sure makes your setup applet code look complicated */
/*  and hides the real reason!  So I've done a sort of back buffer screen class, which we    */
/*  can use to render pixels fastly....for our cool effects...and its all in there...settup  */
/*  and rendering.                                                                           */
/*                                                                                           */
/*********************************************************************************************/
// Few tiny comments
// We need to extend from Applet so we can use, two api's.  createImage(..) and drawImage(..)

class Screen extends Applet
{
   Image m_image;
   int m_pixels[];
   
   int m_width=0;
   int m_height=0;

   MemoryImageSource m_p; 
   public Screen(int width, int height)
   {
     m_pixels = new int[width*height];
     m_width = width;
     m_height = height;
     // Init MemoryImageSource - pixels will be in 32bit ARGB format
		 DirectColorModel dcm=new DirectColorModel(32, 0xff0000, 0xff00, 0xff);
     m_p = new MemoryImageSource(/*width*/m_width,/*height*/m_height,dcm,m_pixels,0,/*width*/m_width);
     m_p.setAnimated(true);
     m_p.setFullBufferUpdates(true);
     m_image = createImage(m_p);
   }// End CScreen(..) constuctor

   public void Render(Graphics g)
   {
      m_p.newPixels(0,0,/*width*/m_width,/*height*/m_height);
      g.drawImage(m_image,0,0, this);
   }// End Render(..)
   
   public int[] GetPixels()
   {
      return m_pixels;
   }// End GetPixels()
   
   
   public int GetWidth() { return m_width; };
   public int GetHeight(){ return m_height; };

}// End Screen(..)


  // Overwrite imageUpdate
  /*
  public boolean imageUpdate(Image image, int i1, int j1, int k, int i2, int j2) {
    return true;
  }
  */
  
  
  

