/************
 *********
 ******
 ****
 ***
 **
 *
 * bluesquares: show flashing squares using SDL -- (c) 2007 Jannis Schnitzer
 * 
 * looks similar to a screensaver from the xscreensaver distribution, I don`t
 * know how its name is--it is Popsquares, I now remember
 * 
 * Files: bluesquares.c
 *
 *
 *
 * 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; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * 
 * 
 **
 ***
 ****
 ******
 *********
 ************/


# include <stdlib.h>
# include <time.h>
# include <SDL/SDL.h>
# include <pthread.h>



typedef enum _bool { false, true } bool;

int done;
int begin;

int DELAY_BETWEEN_UPDATE;
# define DELAY_BETWEEN_RESTART 40

# define MAX_SLEEP_AFTER_START 3500

# define debugfile stderr

# ifndef DEBUG
void dummy ( void ) {}
# define debug( a, ... ) dummy ()
# else
# define debug( a, ... ) fprintf ( a, __VA_ARGS__ )
# endif

struct tp
{
    void *one;
    void *two;
};

int SIX;
int FOUR;

int SC_W;
int SC_H;

int SPACE;

int INVERSE;

int RED;
int GREEN;
int BLUE;

int START;
int END;

int FULLSCREEN;
int LIST;

# define LINUX
# define RN_FILE "/dev/urandom"

void updaterect ( SDL_Surface *screen, SDL_Rect r )
{
    while ( !begin )
	SDL_Delay ( 20 );

    SDL_Delay ( rand () % MAX_SLEEP_AFTER_START );
    
    while ( !done )
    {
	int i;

	debug ( debugfile, "Restarting %d, %d, %d, %d!\n",
		  r.w, r.h, r.x, r.y );

	SDL_Delay ( DELAY_BETWEEN_RESTART +
		    rand () % DELAY_BETWEEN_RESTART + rand () % 1000 );

	if ( INVERSE )
	{
	    for ( i = START; i >= END; i-- )
	    {
		SDL_FillRect (
		    screen, &r, SDL_MapRGB ( screen->format, RED * i, GREEN * i, BLUE * i ) );
		
		if ( DELAY_BETWEEN_UPDATE )
		    SDL_Delay ( DELAY_BETWEEN_UPDATE + rand () % DELAY_BETWEEN_UPDATE * 2 );
		else
		{
       		    struct timespec t;
		    t.tv_sec = 0;
		    t.tv_nsec = 5;
		    nanosleep ( &t, NULL );
		}
	    }
	}
	else
	{
	    for ( i = START; i <= END; i++ )
	    {
		SDL_FillRect (
		    screen, &r, SDL_MapRGB ( screen->format, RED * i, GREEN * i, BLUE * i ) );
		
		if ( DELAY_BETWEEN_UPDATE )
		    SDL_Delay ( DELAY_BETWEEN_UPDATE + rand () % DELAY_BETWEEN_UPDATE * 2 );
		else
		{
       		    struct timespec t;
		    t.tv_sec = 0;
		    t.tv_nsec = 5;
		    nanosleep ( &t, NULL );
		}
	    }
	}
    }
}


void call_ur ( void *pt )
{
    struct tp t = *( ( struct tp * ) pt );

    SDL_Surface *screen;
    SDL_Rect     r;

    screen = ( SDL_Surface * ) t.one;
    r      = ( SDL_Rect )  *( ( SDL_Rect * ) t.two );

    updaterect ( screen, r );
}


int help ( const char *name )
{
# define hpf( ... ) fprintf ( stderr, __VA_ARGS__ ), fprintf ( stderr, "\n" )
    hpf ( "BlueSquares (c) by Jannis Schnitzer, 18.3.2007" );
    hpf ( "Usage: %s {boolean-option} {value-option value}", name );
    hpf ( "Options are:" );
    hpf ( "red          Activate red color" );
    hpf ( "green        Activate green color" );
    hpf ( "blue         Activate blue color" );
    hpf ( "width [n]    How many tiles to draw horizontally" );
    hpf ( "height [n]   How many tiles to draw vertically" );
    hpf ( "space [n]    How much space to draw at the borders of each tile" );
    hpf ( "start [0..255] Start value for the colors" );
    hpf ( "end [0..255] End value for the colors" );
    hpf ( "delay [0..10] Delay between single fading steps" );
    hpf ( "             (lower value -> faster fade)" );
    hpf ( "inverse      slide from light to dark, not from dark to light" );
    hpf ( "fullscreen [n] "
	  "use fullscreen mode [n], show modes with \"list\"" );
    hpf ( "list         show available fullscreen modes" );
    hpf ( "help         show this help" );
    hpf ( "---------------------------" );
    hpf ( "Examples:" );
    hpf ( "%s red green blue -> slides from black to white, 6x4 tiles",
	  name );
    hpf ( "%s end 200 green inverse -> slide from light green to black",
	  name );
# undef hpf

    return EXIT_SUCCESS;
}


int list ( SDL_Rect **modi )
{
    int i;
    for ( i = 0; modi[i]; i++ )
    {
	if ( !i )
	    fprintf ( stderr, "zero:    %dx%d\n", modi[i]->w, modi[i]->h );
	else
	    fprintf ( stderr, "%4d:    %dx%d\n", i, modi[i]->w, modi[i]->h );
    }

    return EXIT_SUCCESS;
}


int main ( int argc, char *argv[] )
{
    SDL_Surface *screen;
    SDL_Event event;

    int i, j;

    int st_ = false;
    int en_ = false;
    int w_  = false;
    int h_  = false;
    int sp_ = false;
    int fs_ = false;
    int dl_ = false;

    SDL_Rect **square;
    SDL_Rect **modi;

    int n_modi;

# ifdef LINUX
    FILE *rn_file = fopen ( RN_FILE, "r" );

    srand ( getc ( rn_file ) + ( 256 * getc ( rn_file ) ) );

    fclose ( rn_file );
# else
    srand ( time ( NULL ) );
# endif

    if ( SDL_Init ( SDL_INIT_VIDEO ) == -1 )
    {
        printf ( "Can't init SDL:  %s\n", SDL_GetError () );
        exit   ( EXIT_FAILURE );
    }

    atexit ( SDL_Quit );


    modi = SDL_ListModes ( NULL, SDL_HWSURFACE | SDL_FULLSCREEN );
    if ( modi == NULL )
	exit ( EXIT_FAILURE );
    else if ( modi == ( SDL_Rect** ) -1 )
	exit ( EXIT_FAILURE );

    for ( n_modi = 0; modi[n_modi]; n_modi++ )
	;

    done  = false;
    begin = false;

    SIX  = 6;
    FOUR = 4;

    RED = GREEN = BLUE = false;
    INVERSE = false;

    START = 0;
    END   = 255;

    FULLSCREEN = false;
    SC_W = 640;
    SC_H = 480;

    SPACE = 6;

    DELAY_BETWEEN_UPDATE = 1;
 
    for ( i = 0; i < argc; i++ )
    {
	if ( w_ || h_   )
	{
	    int n = atoi ( argv[i] );
	    
	    if ( n <= 0 || n > 50 )
		n = -1;

	    if ( n != -1 )
	    {
		if ( w_ )
		    SIX  = n;
		else
		    FOUR = n;
	    }

	    w_ = h_ = false;
	}

	else if ( st_ || en_ )
	{
	    int n = atoi ( argv[i] );
	    
	    if ( n < 1 || n > 256 )
	    {
		if ( strcmp ( argv[i], "zero" ) == 0 )
		    n = 0;
		else
		    n = -1;
	    }

	    if ( n != -1 )
	    {
		if ( st_ )
		    START = n;
		else
		    END   = n;
	    }

	    st_ = en_ = false;
	}

	else if ( sp_ )
	{
	    int n = atoi ( argv[i] );
	    
	    if ( n < 1 || n > SC_W || n > SC_H )
	    {
		if ( strcmp ( argv[i], "zero" ) == 0 )
		    n = 0;
		else
		    n = -1;
	    }

	    if ( n != -1 )
		SPACE = n;

	    sp_ = false;
	}

	else if ( fs_ )
	{
	    int n = atoi ( argv[i] );
	    
	    if ( n < 1 || n > n_modi )
	    {
		if ( strcmp ( argv[i], "zero" ) == 0 )
		    n = 0;
		else
		    n = -1;
	    }

	    if ( n != -1 )
	    {
		FULLSCREEN = true;
		SC_W       = modi[n]->w;
		SC_H       = modi[n]->h;
	    }

	    fs_ = false;
	}

	else if ( dl_ )
	{
	    int n = atoi ( argv[i] );
	    
	    if ( n < 1 || n > 10 )
	    {
		if ( strcmp ( argv[i], "zero" ) == 0 )
		    n = 0;
		else
		    n = -1;
	    }

	    if ( n != -1 )
	    {
		DELAY_BETWEEN_UPDATE = n;
	    }

	    dl_ = false;
	}

	else if ( strcmp ( argv[i], "help" ) == 0 )
	    exit ( help ( argv[0] ) );
	
	else if ( strcmp ( argv[i], "inverse" ) == 0 )
	    INVERSE = true;

	else if ( strcmp ( argv[i], "red" ) == 0 )
	    RED = true;
	
	else if ( strcmp ( argv[i], "green" ) == 0 )
	    GREEN = true;
	
	else if ( strcmp ( argv[i], "blue" ) == 0 )
	    BLUE = true;
	
	else if ( strcmp ( argv[i], "start" ) == 0 )
	    st_ = true;
	
	else if ( strcmp ( argv[i], "end" ) == 0 )
	    en_ = true;

	else if ( strcmp ( argv[i], "width" ) == 0 )
	    w_ = true;

	else if ( strcmp ( argv[i], "height" ) == 0 )
	    h_ = true;

	else if ( strcmp ( argv[i], "space" ) == 0 )
	    sp_ = true;

	else if ( strcmp ( argv[i], "fullscreen" ) == 0 )
	    fs_ = true;

	else if ( strcmp ( argv[i], "list" ) == 0 )
	    exit ( list ( modi ) );

	else if ( strcmp ( argv[i], "delay" ) == 0 )
	    dl_ = true;
    }
    
    square = calloc ( SIX, sizeof ( SDL_Rect * ) );
    if ( !square )
	exit ( EXIT_FAILURE );

    for ( i = 0; i < SIX; i++ )
    {
	square[i] = calloc ( FOUR, sizeof ( SDL_Rect ) );
	if ( !square[i] )
	    exit ( EXIT_FAILURE );
    }

    if ( !RED && !GREEN && !BLUE )
	RED = GREEN = BLUE = true;
    
    if ( ( INVERSE && START < END ) || ( !INVERSE && START > END ) )
    {
	int tmp;
	tmp   = START;
	START = END;
	END   = tmp;
    }


    printf ( "start is %d, end is %d, red is %s, green is %s, blue is %s\n",
	     START, END,
	     ( RED   ) ? ( "activated" ) : ( "disabled" ),
	     ( GREEN ) ? ( "activated" ) : ( "disabled" ),
	     (  BLUE ) ? ( "activated" ) : ( "disabled" ) );

    if ( FULLSCREEN )
	screen = SDL_SetVideoMode (
	    SC_W, SC_H, 16, SDL_HWSURFACE | SDL_FULLSCREEN );
    else
	screen = SDL_SetVideoMode (
	    SC_W, SC_H, 16, SDL_HWSURFACE );

    SDL_FillRect (
	screen, NULL,
	SDL_MapRGB (
	    screen->format,
	    RED   * START,
	    GREEN * START,
	    BLUE  * START ) );
    

    if ( !screen )
    {
        printf ( "Can't set video mode: %s\n", SDL_GetError () );
        exit   ( EXIT_FAILURE );
    }

    for ( i = 0; i < SIX; i++ )
    {
	for ( j = 0; j < FOUR; j++ )
	{
	    debug ( debugfile, "Beginning with initialisation\n" );

	    SDL_Rect *p = &( square[i][j] );

	    struct tp *t = malloc ( sizeof ( struct tp ) );

	    pthread_t thread;
	    
	    if ( !t )
		exit ( EXIT_FAILURE );

	    t->one = screen;
	    t->two = ( void * ) malloc ( sizeof ( *p ) );

	    if ( !t->two )
		exit ( EXIT_FAILURE );

	    p->x = i * ( SC_W / SIX  ) + SPACE;
	    p->y = j * ( SC_H / FOUR ) + SPACE;
	    
	    p->w = ( SC_W / SIX  ) - SPACE;
	    p->h = ( SC_H / FOUR ) - SPACE;

	    memcpy ( t->two, ( void * ) p, sizeof ( *p ) );
	    
	    debug ( debugfile, "We have following: w: %d, h: %d, x: %d, y: %d\n",
		      p->w, p->h, p->x, p->y );

	    pthread_create ( &thread, NULL,
			     ( void * ( * ) ( void * ) ) call_ur,
			     ( void * ) t );

	    debug ( debugfile, "ended initialisation for %d, %d\n", i, j );
	}
    }
    
    begin = true;

    while ( !done )
    {
        while ( SDL_PollEvent ( &event ) )
	{
            switch ( event.type )
	    {
		case SDL_QUIT:
		case SDL_KEYDOWN:
		case SDL_MOUSEBUTTONDOWN:
		    done = 1;
		    break;
            }
        }

	SDL_Flip ( screen );
	
	SDL_Delay ( 10 );
    }

    exit ( EXIT_SUCCESS );
}
