// This is an old PLASMA effect from The Demo Effect Collection
// Converted from SDL to SDL2 on the 11-Jan-2023
// Fixed few bugs
// Update from the 17-March-2026
// 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


/* Copyright (C) 2002 W.P. van Paassen - peter@paassen.tmfweb.nl

   This program is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 2 of the License, or (at your
   option) any later version.

   This program is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not, write to the Free
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */


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


#define EXE_NAME "TDEC_plasma"
// Screen dimension constants
#define SCREEN_WIDTH 480
#define SCREEN_HEIGHT 360


static Uint16 pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0, tpos1, tpos2, tpos3, tpos4;
static int aSin[512];
static SDL_Color colors[256];


SDL_Surface *paletSurface;


void init()
{
  int i;
  float rad;

 
  // create sin lookup table
  for (i = 0; i < 512; i++)
  {
    rad =  ((float)i * 0.703125) * 0.0174532; // 360 / 512 * degree to rad, 360 degrees spread over 512 values to be able to use AND 512-1 instead of using modulo 360
    aSin[i] = sin(rad) * 1024;           // using fixed point math with 1024 as base
  }


  // create a palette
  for (i = 0; i < 64; i++)
  {
    colors[i].r = i << 2;
    colors[i].g = 255 - ((i << 2) + 1);
    colors[i].b = 0;


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


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


    colors[i+192].r = 0;
    colors[i+192].g = (i << 2) + 1;
    colors[i+192].b = 0;
  }


  //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 c,
      oldtime,
      newtime,
      phase;
  Uint16 x,y;
  Uint8 myIndex,
        *myImage;
  SDL_Window *myWindow;
  SDL_Surface *screenSurface;
  unsigned int framecount;
  struct timeval tv;


  if (argc > 1)
  {
    printf("Retro Plasma 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));


  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);


  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;
    }


    // This is where it all happens
    // DRAW PLASMA


    tpos4 = pos4;
    tpos3 = pos3;


    myImage = (Uint8 *)(paletSurface->pixels);
      

    for (y = 0; y < SCREEN_HEIGHT; y++)
    {
      tpos1 = pos1 + 5;
      tpos2 = pos2 + 3;


      tpos3 &= 511;
      tpos4 &= 511;


      for (x = 0; x < SCREEN_WIDTH; x++)
      {
        tpos1 &= 511;
        tpos2 &= 511;


        c = aSin[tpos1] + aSin[tpos2] + aSin[tpos3] + aSin[tpos4]; // actual plasma calculation


        myIndex = 128 + (c >> 4);    /* fixed point multiplication but optimized so basically it says (c * (64 * 1024) / (1024 * 1024)), c is already multiplied by 1024 */


        *myImage++ = myIndex;


        tpos1 += 5; 
        tpos2 += 3; 
      }
      tpos4 += 3;
      tpos3 += 1;
    }


    // MOVE PLASMA
    pos1 +=9;
    pos3 +=8;


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


    //SDL_Delay(10);


    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);
}
