#include <workbench/workbench.h>
#include <workbench/startup.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/icon.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <m68881.h>
#include <math.h>
#include <signal.h>
#include <time.h>

#include <DoArgs.h>

#include "crinkle.h"
#include "paint.h"
#include "global.h"
#include "patchlevel.h"

#define VERSION 2
#define SIDE 1.0

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE (!FALSE)
#endif

/* -------------------------------------------------------------------- */
/* update strips														*/
/* -------------------------------------------------------------------- */

Col *next_col( int paint, int reflec )
{
	Col	*res;
	int	i, offset = 0;
  
	if ( paint ) {
		if( reflec ) {
			res = mirror( a_strip, b_strip, shadow );
		}
		else {
			res = camera( a_strip, b_strip, shadow );
		}
	}
	else {
		res = makemap( a_strip, b_strip, shadow );
	}
	free( a_strip );
	a_strip = b_strip;
	b_strip = extract( next_strip( top ) );

	/* ---------------------------------------------------------------- */
	/* update the shadows												*/
	/* shadow_slip is the Y component of the light vector.				*/
	/* The shadows can only step an integer number of points in the Y	*/
	/* direction so we maintain shadow_register as the deviation		*/
	/* between where the shadows are and where they should be. When the	*/
	/*  magnitude of this gets larger then 1 the shadows are slipped by	*/
	/*  the required number of points.									*/
	/* This will not work for very oblique angles so the horizontal		*/
	/* angle of illumination should be constrained.						*/
	/* ---------------------------------------------------------------- */

	shadow_register += shadow_slip;
	if ( shadow_register >= 1.0 ) {

		/* ------------------------------------------------------------ */
		/* negative offset												*/
		/* ------------------------------------------------------------ */

		while ( shadow_register >= 1.0 ) {
			shadow_register -= 1.0;
			offset++;
		}
		for ( i = width - 1; i >= offset; i-- ) {
			shadow[i] = shadow[i-offset] - delta_shadow;
			if ( shadow[i] < b_strip[i] ) {
				shadow[i] = b_strip[i];
			}

			/* -------------------------------------------------------- */
			/* stop shadow at sea level									*/
			/* -------------------------------------------------------- */

			if ( shadow[i] < sealevel ) {
				shadow[i] = sealevel;
			}
		}

		for ( i = 0; i < offset; i++ ) {
			shadow[i] = b_strip[i];

			/* -------------------------------------------------------- */
			/* stop shadow at sea level									*/
			/* -------------------------------------------------------- */

			if ( shadow[i] < sealevel ) {
				shadow[i] = sealevel;
			}
		}
	}
	else if ( shadow_register <= -1.0 ) {

		/* ------------------------------------------------------------ */
		/* positive offset												*/
		/* ------------------------------------------------------------ */

		while ( shadow_register <= -1.0 ) {
			shadow_register += 1.0;
			offset++;
		}
		for ( i = 0; i < width - offset; i++ ) {
			shadow[i] = shadow[i+offset] - delta_shadow;
			if ( shadow[i] < b_strip[i] ) {
				shadow[i] = b_strip[i];
			}

			/* -------------------------------------------------------- */
			/* stop shadow at sea level									*/
			/* -------------------------------------------------------- */

			if ( shadow[i] < sealevel ) {
				shadow[i] = sealevel;
			}
		}
		for( ; i < width; i++ ) {
			shadow[i] = b_strip[i];

			/* -------------------------------------------------------- */
			/* stop shadow at sea level									*/
			/* -------------------------------------------------------- */

			if ( shadow[i] < sealevel ) {
				shadow[i] = sealevel;
			}
		}
	}
	else {

		/* ------------------------------------------------------------ */
		/* no offset													*/
		/* ------------------------------------------------------------ */

		for ( i = 0; i < width; i++ ) {
			shadow[i] -= delta_shadow;
			if ( shadow[i] < b_strip[i] ) {
				shadow[i] = b_strip[i];
			}

			/* -------------------------------------------------------- */
			/* stop shadow at sea level									*/
			/* -------------------------------------------------------- */

			if ( shadow[i] < sealevel ) {
				shadow[i] = sealevel;
			}
		}
	}
	return res;
}

void init_graphics( int, int *, int *, int, Gun *, Gun *, Gun *, char *, ULONG, char * );
void finish_graphics( void );
void plot_pixel(int, int, unsigned char );
void scroll_screen( int );
void zap_events( int );
void finish_prog( int );

int		s_height,
		s_width,
		mapwid;

void plot_column( int p, int map, int reflec, int snooze )
{
	Col	*l;
	int	j;
  
	l = next_col( 1 - map, reflec ); 
	if ( map ) {
		for ( j = 0; j < s_height - mapwid; j++ ) {
			plot_pixel( p, s_height - 1 - j, BLACK );
		}
		for ( j = 0; j < mapwid; j++ ) {
			plot_pixel( p, mapwid - 1 - j, l[j] );
		}
	}
	else {
		for ( j = 0; j < height; j++ ) {

			/* -------------------------------------------------------- */
			/* we assume that the scroll routine fills the new region	*/
			/* with a SKY value. This allows us to use a textured sky	*/
			/* for B/W displays											*/
			/* -------------------------------------------------------- */

			if ( l[j] != SKY ) {
				plot_pixel( p, s_height - 1 - j, l[j] );
			}
		}
	}
	free( l );
	zap_events( snooze );
}

extern LONG activepri, inactivepri;

static char *pubscreen, *displaymode;
static ULONG depth;

void main( int argc, char **argv )
{
	int		i, p;
	int		repeat;
	int		map;
	int		reflec;
	int		root;

	Gun		*clut[3];
	DAKey	*dak;

	double	minv_phi		=    0.0,
			maxv_phi		=  180.0 / 180.0 * PI,
			def_phi			=   40.0 / 180.0 * PI,
			minv_alpha		=  -60.0 / 180.0 * PI,
			maxv_alpha		=   60.0 / 180.0 * PI,
			def_alpha		=    0.0,
			def_stretch		=    0.6,
			def_shift		=    0.5,
			def_sealevel	=    0.0,
			def_forceheight	=	-1.0,
			def_contour		=    0.3,
			def_altitude	=    2.5,
			def_distance	=    4.0,
			minv_contrast	=    0.0,
			def_contrast	=    1.0,
			minv_ambient	=    0.0,
			maxv_ambient	=    1.0,
			def_ambient		=    0.3,
			minv_vfract		=    0.0,
			def_vfract		=    0.6,
			minv_fdim		=    0.5,
			maxv_fdim		=    1.0,
			def_fdim		=    0.65,
			def_mix			=    0.0,
			def_midmix		=    0.0;

	BOOL	passed_n_col		= FALSE,
			passed_band_size	= FALSE;

	/* ---------------------------------------------------------------- */
	/* handle command line/tool type arguents							*/
	/* ---------------------------------------------------------------- */

	dak = DoArgs( argc, argv,

		DA_TTIconName,		argc ? argv[0] : ((struct WBStartup *) argv)->sm_ArgList[0].wa_Name,

		DA_Name,			"BACKDROP",
		DA_Type,			DAT_BOOL,
		DA_Result,			&root,
		TAG_DONE,

		DA_Name,			"WIDTH",
		DA_Type,			DAT_UNSIGNED,
		DA_Minval,			40,
		DA_Default,			320,
		DA_Result,			&s_width,
		TAG_DONE,

		DA_Name,			"HEIGHT",
		DA_Type,			DAT_UNSIGNED,
		DA_Minval,			40,
		DA_Default,			240,
		DA_Result,			&s_height,
		TAG_DONE,

		DA_Name,			"PUBSCREEN",
		DA_Type,			DAT_STRING,
		DA_Default,			"Workbench",
		DA_Result,			&pubscreen,
		DA_ProvideMem,		TRUE,
		TAG_DONE,

		DA_Name,			"DISPLAYMODE",
		DA_Type,			DAT_STRING,
		DA_Result,			&displaymode,
		DA_ProvideMem,		TRUE,
		TAG_DONE,

		DA_Name,			"DEPTH",
		DA_Type,			DAT_UNSIGNED,
		DA_Default,			0,
		DA_Result,			&depth,
		TAG_DONE,

		DA_Name,			"MAP",
		DA_Type,			DAT_BOOL,
		DA_Result,			&map,
		TAG_DONE,

		DA_Name,			"REFLECTIONS",
		DA_Type,			DAT_BOOL,
		DA_Result,			&reflec,
		TAG_DONE,

		DA_Name,			"SCROLLCOLUMNS",
		DA_Type,			DAT_UNSIGNED,
		DA_Minval,			2,
		DA_Default,			20,
		DA_Result,			&repeat,
		TAG_DONE,

		DA_Name,			"BANDSIZE",
		DA_Type,			DAT_UNSIGNED,
		DA_Minval,			2,
		DA_Result,			&band_size,
		DA_Passed,			&passed_band_size,
		TAG_DONE,

		DA_Name,			"COLOURS",
		DA_Type,			DAT_UNSIGNED,
		DA_Minval,			MIN_COL,
		DA_Result,			&n_col,
		DA_Passed,			&passed_n_col,
		TAG_DONE,

		DA_Name,			"SLEEP",
		DA_Type,			DAT_UNSIGNED,
		DA_Result,			&snooze_time,
		DA_Default,			0,
		TAG_DONE,

		DA_Name,			"VLIGHTANGLE",
		DA_Type,			DAT_ANGLE,
		DA_Minval,			&minv_phi,
		DA_Maxval,			&maxv_phi,
		DA_Default,			&def_phi,
		DA_Result,			&phi,
		TAG_DONE,

		DA_Name,			"HLIGHTANGLE",
		DA_Type,			DAT_ANGLE,
		DA_Minval,			&minv_alpha,
		DA_Maxval,			&maxv_alpha,
		DA_Default,			&def_alpha,
		DA_Result,			&alpha,
		TAG_DONE,

		DA_Name,			"VSTRETCH",
		DA_Type,			DAT_DOUBLE,
		DA_Default,			&def_stretch,
		DA_Result,			&stretch,
		TAG_DONE,

		DA_Name,			"VSHIFT",
		DA_Type,			DAT_DOUBLE,
		DA_Default,			&def_shift,
		DA_Result,			&shift,
		TAG_DONE,

		DA_Name,			"SEALEVEL",
		DA_Type,			DAT_DOUBLE,
		DA_Default,			&def_sealevel,
		DA_Result,			&sealevel,
		TAG_DONE,

		DA_Name,			"SLOPE",
		DA_Type,			DAT_UNSIGNED,
		DA_Minval,			2,
		DA_Default,			2,
		DA_Result,			&slope,
		TAG_DONE,

		DA_Name,			"FORCEHEIGHT",
		DA_Type,			DAT_DOUBLE,
		DA_Default,			&def_forceheight,
		DA_Result,			&forceheight,
		TAG_DONE,

		DA_Name,			"CONTOUR",
		DA_Type,			DAT_DOUBLE,
		DA_Default,			&def_contour,
		DA_Result,			&contour,
		TAG_DONE,

		DA_Name,			"ALTITUDE",
		DA_Type,			DAT_DOUBLE,
		DA_Default,			&def_altitude,
		DA_Result,			&altitude,
		TAG_DONE,

		DA_Name,			"DISTANCE",
		DA_Type,			DAT_DOUBLE,
		DA_Default,			&def_distance,
		DA_Result,			&distance,
		TAG_DONE,

		DA_Name,			"CONTRAST",
		DA_Type,			DAT_DOUBLE,
		DA_Minval,			&minv_contrast,
		DA_Default,			&def_contrast,
		DA_Result,			&contrast,
		TAG_DONE,

		DA_Name,			"AMBIENT",
		DA_Type,			DAT_DOUBLE,
		DA_Minval,			&minv_ambient,
		DA_Maxval,			&maxv_ambient,
		DA_Default,			&def_ambient,
		DA_Result,			&ambient,
		TAG_DONE,

		DA_Name,			"VFRACT",
		DA_Type,			DAT_DOUBLE,
		DA_Minval,			&minv_vfract,
		DA_Default,			&def_vfract,
		DA_Result,			&vfract,
		TAG_DONE,

		DA_Name,			"FDIM",
		DA_Type,			DAT_DOUBLE,
		DA_Minval,			&minv_fdim,
		DA_Maxval,			&maxv_fdim,
		DA_Default,			&def_fdim,
		DA_Result,			&fdim,
		TAG_DONE,

		DA_Name,			"SEED",
		DA_Type,			DAT_INTEGER,
		DA_Default,			0,
		DA_Result,			&seed,
		TAG_DONE,

		DA_Name,			"LEVELS",
		DA_Type,			DAT_UNSIGNED,
		DA_Minval,			2,
		DA_Default,			10,
		DA_Result,			&levels,
		TAG_DONE,

		DA_Name,			"CROSS",
		DA_Type,			DAT_BOOL,
		DA_Result,			&cross,
		TAG_DONE,

		DA_Name,			"SMOOTH",
		DA_Type,			DAT_UNSIGNED,
		DA_Default,			1,
		DA_Result,			&smooth,
		TAG_DONE,

		DA_Name,			"MIX",
		DA_Type,			DAT_DOUBLE,
		DA_Default,			&def_mix,
		DA_Result,			&mix,
		TAG_DONE,

		DA_Name,			"MIDMIX",
		DA_Type,			DAT_DOUBLE,
		DA_Default,			&def_midmix,
		DA_Result,			&midmix,
		TAG_DONE,

		DA_Name,			"STOP",
		DA_Type,			DAT_UNSIGNED,
		DA_Default,			2,
		DA_Result,			&stop,
		TAG_DONE,

		DA_Name,			"ACTIVEPRI",
		DA_Type,			DAT_INTEGER,
		DA_Minval,			-127,
		DA_Maxval,			128,
		DA_Default,			0,
		DA_Result,			&activepri,
		TAG_DONE,

		DA_Name,			"INACTIVEPRI",
		DA_Type,			DAT_INTEGER,
		DA_Minval,			-127,
		DA_Maxval,			128,
		DA_Default,			-25,
		DA_Result,			&inactivepri,
		TAG_DONE,

		TAG_DONE
	);

	if ( dak ) {
		if ( passed_n_col ) {
			band_size = ( n_col - BAND_BASE ) / N_BANDS;
		}
		else if ( passed_band_size ) {
			n_col = BAND_BASE + N_BANDS * band_size;
		}
		else {
			band_size = BAND_SIZE;
			n_col     = BAND_BASE + N_BANDS * BAND_SIZE;
		}

		if ( repeat & 1 ) repeat++;
	}	
	else {
		PutStr( "Error: Cannot allocate argument array!\n" );
		exit( 5 );
	}

	for ( i = 0; i < 3; i++ ) {
		clut[i] = (Gun *) malloc( n_col * sizeof(Gun) );
		if( ! clut[i] ) {
			PutStr( "malloc failed for clut\n" );
			exit( 1 );
		}
	}
	set_clut( n_col, clut[0], clut[1], clut[2] );
	init_graphics( root, &s_width, &s_height, n_col, clut[0], clut[1], clut[2], displaymode, depth, pubscreen );
	for ( i = 0; i < 3; i++ ) {
		free( clut[i] );
	}

	height = s_height;
    
	srand48( seed ? seed : time( NULL ) );

	init_artist_variables();

	if ( s_height > width ) {
		mapwid = width;
	}
	else {
		mapwid = s_height;
	}
	if ( repeat > 0 ) {
		for ( p = 0; p < s_width; p++ ) {
			plot_column( p, map, reflec, 0 );
		}
	}
	else {
		for ( p = s_width - 1; p >= 0; p-- ) {
			plot_column( p, map, reflec, 0 );
		}
	}
	while ( TRUE ) {

		/* ------------------------------------------------------------ */
		/* do the scroll												*/
		/* ------------------------------------------------------------ */

		scroll_screen( repeat );
		if( repeat > 0) {
			for ( p = s_width - repeat; p < s_width - 1; p++ ) {
				plot_column( p, map, reflec, 0 );
			}
		}
		else {
			for ( p = -1 - repeat; p >= 0; p-- ) {
				plot_column( p, map, reflec, 0 );
			}
		}
		plot_column( p, map, reflec, snooze_time );
	}

	FreeDAKey( dak );
}
