/*********************************************************************************************/
/*                                                                                           */
/*  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;

class Colour
{
  public int r,g,b;
}

public class fire 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();

   void initPattern()
   {
      fire = new int[ scr.GetWidth() * scr.GetHeight() ];
      colours = new Colour[ 256 ];
      
      for(int t=0; t<256; t++)
         colours[t] = new Colour();

      for (int 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

   }//End initPattern()


   void Pattern(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 Fire Graphics Demo\n" );
   }// End of start()
	
}// 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;
  }
  */
  
  
  

