// This is an old FIRE effect from The Demo Effect Collection
// Converted from SDL to SDL2 on the 8-May-2024
// Fixed few bugs
// Update from the 17-March-2026
// Original author: W.P. van Paassen
// https://demo-effects.sourceforge.net/
// Functions like TDEC_set_video() and TDEC_init_timer() from "COMMON/tdec.h" are not used
// No SDL2 renderer used


#include <SDL.h>
#include <stdio.h>
#include <sys/time.h>


#define EXE_NAME "TDEC_fire"
#define FIRE_HEIGHT 80


// Screen dimension constants
#define SCREEN_WIDTH 480
#define SCREEN_HEIGHT 360


static SDL_Color colors[256];


SDL_Surface *paletSurface;


void init()
{
  int i;


  /* create a suitable fire palette, this is crucial for a good effect */
  /* black to blue, blue to red, red to yellow, yellow to white */


  for (i = 0; i < 32; i++)
  {
      /* black to blue, 32 values */
      colors[i].r = 0;
      colors[i].g = 0;
      colors[i].b = i << 1;

      /* blue to red, 32 values */
      colors[i + 32].r = i << 3;
      colors[i + 32].g = 0;
      colors[i + 32].b = 64 - (i << 1);

      /* red to yellow, 32 values */
      colors[i + 64].r = 255;
      colors[i + 64].g = i << 3;
      colors[i + 64].b = 0;

      /* yellow to white, 162 */
      colors[i + 96].r = 255;
      colors[i + 96].g = 255;
      colors[i + 96].b = i << 2;


      colors[i + 128].r = 255;
      colors[i + 128].g = 255;
      colors[i + 128].b = 64 + (i << 2);


      colors[i + 160].r = 255;
      colors[i + 160].g = 255;
      colors[i + 160].b = 128 + (i << 2);


      colors[i + 192].r = 255;
      colors[i + 192].g = 255;
      colors[i + 192].b = 192 + i;


      colors[i + 224].r = 255;
      colors[i + 224].g = 255;
      colors[i + 224].b = 224 + i;
  }


  //SDL_SetPalette(screen, SDL_LOGPAL | SDL_PHYSPAL, colors, 0, 256);
  SDL_SetPaletteColors(paletSurface->format->palette, colors, 0, 256);
}


int process_events( void )
{
  SDL_Event myEvent;


  while( SDL_PollEvent( &myEvent ) )
  {
    switch( myEvent.type )
    {
      case SDL_QUIT:
        // SDL_QUIT technically means pressing window's closing gadget
        return(1);
      case SDL_KEYDOWN:
        if(myEvent.key.keysym.sym==SDLK_ESCAPE)
        {
          return(1);
        }
    }
  }
  return(0);
}


int main( int argc, char* args[] )
{
  int x,
      y,
      oldtime,
      newtime,
      phase;
  Uint8 *myImage;
  Uint16 temp;
  Uint32 color;
  SDL_Window *myWindow;
  SDL_Surface *screenSurface;
  SDL_Rect srect,
           drect;
  unsigned int framecount;
  struct timeval tv;


  if (argc > 1)
  {
    printf("Retro Fire Effect - W.P. van Paassen - 2002\n");
    exit(EXIT_SUCCESS);
  }


  // Initialize SDL
  if( SDL_Init( SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0 )
  {
    fprintf(stderr,"%s: SDL could not initialize! SDL_Error: %s\n", EXE_NAME, SDL_GetError() );
    exit(EXIT_FAILURE);
  }


  // As far as I know, it's not possible to open a palettized window directly
  // We have to create a window, then create a palettized surface and copy
  // it's contents to the window surface each frame
  myWindow = SDL_CreateWindow(EXE_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
    SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
  if(myWindow==NULL)
  {
    fprintf(stderr,"%s: Window could not be created! SDL_Error: %s\n", EXE_NAME, SDL_GetError() );
    SDL_Quit();
    exit(EXIT_FAILURE);
  }
  // Get window surface
  screenSurface=SDL_GetWindowSurface(myWindow);
  printf("Window surface format name: %s\n",SDL_GetPixelFormatName(screenSurface->format->format));


  // Black color
  color=SDL_MapRGB(screenSurface->format,0x00,0x00,0x00);
  // Fill the whole surface
  SDL_FillRect(screenSurface,NULL,color);


  paletSurface=SDL_CreateRGBSurface(SDL_SWSURFACE,SCREEN_WIDTH,SCREEN_HEIGHT,8,0,0,0,0);       // 8 bits mean a palettized surface
  if(paletSurface==NULL)
  {
    fprintf(stderr,"%s: Can't initialize a SDL2 surface!\n",EXE_NAME);
    SDL_DestroyWindow(myWindow);
    SDL_Quit();
    exit(EXIT_FAILURE);
  }
  printf("Palette surface format name: %s\n",SDL_GetPixelFormatName(paletSurface->format->format));
  printf("Number of palette colors: %d\n",paletSurface->format->palette->ncolors);


  // Black color
  color=SDL_MapRGB(paletSurface->format,0x00,0x00,0x00);
  // Fill the whole surface
  SDL_FillRect(paletSurface,NULL,color);


  init();


  printf("Palette surface lock: ");
  if(SDL_MUSTLOCK(paletSurface))
  {
    printf("needed\n");
    SDL_LockSurface(paletSurface);
  }
  else
  {
    printf("not needed\n");
  }


  phase=0;
  framecount=0;
  gettimeofday(&tv,NULL);
  oldtime=tv.tv_usec;


  for(;;)
  {
    if(process_events())
    {
      break;
    }


    /* draw random bottom line in fire array */


    myImage = (Uint8*)(paletSurface->pixels) + (paletSurface->pitch * (SCREEN_HEIGHT-1));
    for (x = 0; x < SCREEN_WIDTH - 1; x++)
    {
      // Stores either 0 or 255 into the paletSurface
      // Note the probability of maximum heat is slightly lower than the probability of no heat
      // If you change the default value (8), the effect doesn't look this good
      if((rand()%16) > 8)
        *myImage = 255; /* maximum heat */
      else
        *myImage = 0;
      myImage++;
    }


    /* move fire upwards, start at bottom */


    myImage = (Uint8*)(paletSurface->pixels) + (paletSurface->pitch * (SCREEN_HEIGHT-1));
    for (y = 0; y < FIRE_HEIGHT ; y++)
    {
      for (x = 0; x < SCREEN_WIDTH ; x++)
      {
        if (x == 0) /* at the left border */
        {
          temp = *myImage;
          temp += *(myImage + 1);
          temp += *(myImage - SCREEN_WIDTH);
          temp /= 3;
        }
        else if (x == (SCREEN_WIDTH - 1)) /* at the right border */
        {
          temp = *(myImage + x);
          temp += *(myImage - SCREEN_WIDTH + x);
          temp += *(myImage + x - 1);
          temp /= 3;
        }
        else
        {
          temp = *(myImage + x);
          temp += *(myImage + x + 1);
          temp += *(myImage + x - 1);
          temp += *(myImage - SCREEN_WIDTH + x);
          temp >>= 2;
        }
        if (temp > 1)
          temp -= 1; /* decay */


        *(myImage - SCREEN_WIDTH + x) = temp;
      }
      myImage -= SCREEN_WIDTH;
    }


    // Copy the entire surface
    //SDL_BlitSurface(paletSurface,NULL,screenSurface,NULL);


    srect.x=0;
    srect.y=SCREEN_HEIGHT-FIRE_HEIGHT-1;
    srect.w=SCREEN_WIDTH;
    srect.h=FIRE_HEIGHT-2;


    drect.x=0;
    drect.y=SCREEN_HEIGHT-FIRE_HEIGHT+2;     // The original author doesn't display 2 bottom lines
    drect.w=SCREEN_WIDTH;
    drect.h=FIRE_HEIGHT-2;


    SDL_BlitSurface(paletSurface,&srect,screenSurface,&drect);
    SDL_UpdateWindowSurface(myWindow);


    SDL_Delay(20);


    framecount++;
    gettimeofday(&tv,NULL);
    newtime=tv.tv_usec;
    if(oldtime>newtime)
    {
      if(phase==0)
      {
        phase=1;
        framecount=0;
      }
      else
      {
        printf("FPS: %d\n",framecount);
        framecount=0;
      }
    }
    oldtime=newtime;
  }


  if(SDL_MUSTLOCK(paletSurface))
  {
    SDL_UnlockSurface(paletSurface);
  }
  SDL_FreeSurface(paletSurface);
  // Destroy window
  SDL_DestroyWindow(myWindow);
  // Quit SDL subsystems
  SDL_Quit();
  exit(EXIT_SUCCESS);
}
