#include <workbench/workbench.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 "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;

static int imin( int a, int b )
{
	return min( a, b );
}

static int imax( int a, int b )
{
	return max( a, b );
}

static double fmin( double a, double b )
{
	return min( a, b );
}

static double fmax( double a, double b )
{
	return max( a, b );
}

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

	Gun		*clut[3];
	UBYTE	**a;

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


	if ( a = ArgArrayInit( argc, argv ) ) {
		root		= ArgString( a, "BACKDROP", NULL ) != NULL;
		s_width		= imax( 40, ArgInt( a, "WIDTH", 320 ) );
		s_height	= imax( 40, ArgInt( a, "HEIGHT", 240 ) );
		pubscreen	= ArgString( a, "PUBSCREEN", "Workbench" );
		displaymode	= ArgString( a, "DISPLAYMODE", NULL );
		depth		= imax( 0, ArgInt( a, "DEPTH", 0 ) );
		map			= ArgString( a, "MAP", NULL ) != NULL;
		reflec		= ArgString( a, "REFLECTIONS", NULL ) != NULL;
		repeat		= imax( 2, ArgInt( a, "SCROLLCOLUMNS", 20 ) );
		band_size	= imax( 2, ArgInt( a, "BANDSIZE", BAND_SIZE ) );
		n_col		= BAND_BASE + N_BANDS * band_size;
		n_col		= imax( MIN_COL, ArgInt( a, "COLOURS", n_col ) );
		band_size	= ( n_col - BAND_BASE ) / N_BANDS;
		n_col		= BAND_BASE + N_BANDS * band_size;
		snooze_time	= imax( 0, ArgInt( a, "SLEEP", 0 ) );
		phi			= PI * atof( ArgString( a, "VLIGHTANGLE", "40.0" ) ) / 180.0;
		phi			= fmax( 0.0, fmin( PID2, phi ) );
		alpha		= PI * atof( ArgString( a, "HLIGHTANGLE", "0.0" ) ) / 180.0;
		alpha		= fmax( -PI / 3.0, fmin( PI / 3.0, alpha ) );
		stretch		= atof( ArgString( a, "VSTRETCH", "0.6" ) );
		shift		= atof( ArgString( a, "VSHIFT", "0.5" ) );
		sealevel	= atof( ArgString( a, "SEALEVEL", "0.0" ) );
		slope		= imax( 2, ArgInt( a, "SLOPE", 2 ) );
		forceheight	= atof( ArgString( a, "FORCEHEIGHT", "-1.0" ) );
		contour		= atof( ArgString( a, "CONTOUR", "0.3" ) );
		altitude	= atof( ArgString( a, "ALTITUDE", "2.5" ) );
		distance	= atof( ArgString( a, "DISTANCE", "4.0" ) );
		contrast	= fmax( 0.0, atof( ArgString( a, "CONTRAST", "1.0" ) ) );
		ambient		= fmax( 0.0, fmin( 1.0, atof( ArgString( a, "AMBIENT", "0.3" ) ) ) );
		vfract		= fmax( 0.0, atof( ArgString( a, "VFRACT", "0.6" ) ) );
		fdim		= fmax( 0.5, fmin( 1.0, atof( ArgString( a, "FDIM", "0.65" ) ) ) );
		seed		= ArgInt( a, "SEED", 0 );
		levels		= imax( 2, ArgInt( a, "LEVELS", 10 ) );
		cross		= ArgString( a, "CROSS", NULL ) != NULL;
		smooth		= ArgInt( a, "SMOOTH", 1 );
		mix			= atof( ArgString( a, "MIX", "0.0" ) );
		midmix		= atof( ArgString( a, "MIDMIX", "0.0" ) );
		stop		= imax( 0, ArgInt( a, "STOP", 2 ) );
		activepri	= ArgInt( a, "ACTIVEPRI", 0 );
		inactivepri	= ArgInt( a, "INACTIVEPRI", -25 );

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

	ArgArrayDone();
}
