#include "defs.h"
#include <math.h>
#include "randalg.h"


const double deg = M_PI / 180;

#define qt_M_PI   4*M_PI/3

#define star_classes    12
#define planet_types    10
#define avgmoons        4
#define log2avgmoons	2
#define maxbodies	20 * avgmoons

double foundstar_x, foundstar_y, foundstar_z, foundstar_id;
Dword foundstar_sx, foundstar_sy, foundstar_sz;
Word    foundstar_class     = 0;		// 35
Word    foundstar_nop       = 0;		// 37
float  foundstar_ray       = 1000;	// 67
char   foundstar_spin      = 0;          // 18
Word	      foundstar_nob = 0;

Word class_ray[star_classes] = { 5000, 15000, 300, 20000, 15000, 1000, 3000,
				2000, 4000, 1500, 30000, 250 };

Word class_rayvar[star_classes] = { 2000, 10000, 200, 15000, 5000, 1000, 3000,
				   500, 5000, 10000, 1000, 10 };

Word class_act[star_classes] = { 2, 4, 1, 6, 5, 10, 100, 1, 2, 1, 10, 1 };

char class_planets[star_classes] = { 12, 18, 8, 15, 20, 3, 0, 1, 7, 20, 2, 5 };

char   foundstar_p_type       [maxbodies];
Word    foundstar_p_owner	     [maxbodies];
char   foundstar_p_moonid     [maxbodies];
double foundstar_p_ring       [maxbodies];
double foundstar_p_tilt       [maxbodies];
double foundstar_p_ray        [maxbodies];
double foundstar_p_orb_ray    [maxbodies];
double foundstar_p_orb_seed   [maxbodies];
double foundstar_p_orb_tilt   [maxbodies];
double foundstar_p_orb_orient [maxbodies];
double foundstar_p_orb_ecc    [maxbodies];

Word    foundstar_p_rtperiod   [maxbodies];
Word    foundstar_p_rotation   [maxbodies];
Word    foundstar_p_term_start [maxbodies];
Word    foundstar_p_term_end   [maxbodies];

Word planet_possiblemoons[] = { 1, 1, 2, 3, 2, 2, 18, 2, 3, 20, 20 };

const double planet_orb_scaling=  5.0;
const double avg_planet_sizing =  2.4;
const double moon_orb_scaling  = 12.8;
const double avg_moon_sizing   =  1.8;

double avg_planet_ray[] = { 0.007, 0.003, 0.010, 0.011, 0.010,
			    0.008, 0.064, 0.009, 0.012, 0.125,
			    2.000 };

float mindiff = 0.01;

float zrandom (Word range) { return (RANDOM(range) - RANDOM(range)); }

// ---------------------------------------------------------------------------------
long ImulAndAdd(long main, long secondary) {
	long result = 0;
	asm {
		mov edx, dword ptr main
		mov eax, dword ptr secondary
		imul edx
		add edx, eax
		mov dword ptr result, edx
	}
	return result;
}
#define advance 100000
#define halfadvance 50000

void PrepareFromSectorCoords (double sect_x, double sect_y, double sect_z) {
	Dword xz, mixer;
	Dword laststar_x, laststar_y, laststar_z;
	double laststar_id;
	xz = sect_x + sect_z;
	laststar_x = (xz & 0x1ffff) + sect_x - halfadvance;
	laststar_y = ImulAndAdd(laststar_x, xz);
	mixer = xz + laststar_y;
	laststar_y = (laststar_y & 0x1ffff) + sect_y - halfadvance;
	laststar_z = ImulAndAdd(laststar_y, mixer);
	laststar_z = (laststar_z & 0x1ffff) + sect_z - halfadvance;
	laststar_id = ((double)laststar_x)/advance*((double)laststar_y)/advance*((double)laststar_z)/advance;
	foundstar_x=(double)laststar_x;
	foundstar_y=(double)laststar_y;
	foundstar_z=(double)laststar_z;
	foundstar_sx=sect_x;
	foundstar_sy=sect_y;
	foundstar_sz=sect_z;
	foundstar_id=laststar_id;
}

void PrepareFromSectorCoords (Dword sect_x, Dword sect_y, Dword sect_z) {
	PrepareFromSectorCoords((double)sect_x, (double)sect_y, (double)sect_z);
}

FastBool DoesStarExist (double sect_x, double sect_y, double sect_z) {
	FastBool valid=False;
	PrepareFromSectorCoords(sect_x, sect_y, sect_z);
	if (foundstar_x!=0 && foundstar_y!=0 && foundstar_z!=0) {
		Dword rarity_factor;
		double dsect_x = sect_x; dsect_x*=dsect_x;
		double dsect_y = sect_y; dsect_y=30*fabs(dsect_y);
		double dsect_z = sect_z; dsect_z*=dsect_z;
		double distance_from_home = sqrt (dsect_x + dsect_z) + dsect_y;
		rarity_factor = distance_from_home * 0.25e-8;
		rarity_factor = 1 << rarity_factor;
		rarity_factor--;
		Word rarity = (((Word)foundstar_x) + ((Word)foundstar_y) + ((Word)foundstar_z));
		if ((rarity & rarity_factor) == 0) {
			valid=True;
		}
	}
	return valid;
}

void extract_foundstar_infos ()
{
	SRAND (foundstar_id);
	
	foundstar_class = RANDOM (star_classes);
	//if (foundstar_class!=my_foundstar_class) {
	//	DebugPrintf(1, "Error: foundstar_class = %i, my_foundstar_class = %i", (int)foundstar_class, (int)my_foundstar_class);
	//}
	foundstar_ray = ((float)class_ray[foundstar_class] + (float)RANDOM(class_rayvar[foundstar_class])) * 0.001;

	foundstar_spin = 0;
	if (foundstar_class==11) foundstar_spin = RANDOM (30) + 1;
	if (foundstar_class==7) foundstar_spin = RANDOM (12) + 1;
	if (foundstar_class==2) foundstar_spin = RANDOM (4) + 1;
}
void prepare_foundstar ()
{
	Word    n, c, q, r, s, t;
	double key_radius;
	
	double s_m = qt_M_PI * foundstar_ray * foundstar_ray * foundstar_ray * 0.01e-7;

	SRAND ((Dword)foundstar_x%10000*(Dword)foundstar_y%10000*(Dword)foundstar_z%10000);

	foundstar_nop = RANDOM (class_planets[foundstar_class] + 1);

	for (n=0; n<foundstar_nop; n++) {
		foundstar_p_owner[n]	 = -1;
		foundstar_p_orb_orient[n] = (double) deg * (double) RANDOM (360);
		foundstar_p_orb_seed[n]   = 3 * (n*n+1) * foundstar_ray + (float) RANDOM (300 * foundstar_ray) / 100;
		foundstar_p_tilt[n]       = zrandom (10*foundstar_p_orb_seed[n]) / 500;
		foundstar_p_orb_tilt[n]   = zrandom (10*foundstar_p_orb_seed[n]) / 5000;
		foundstar_p_orb_ecc[n]    = 1 - (double) RANDOM (foundstar_p_orb_seed[n] + 10*fabs(foundstar_p_orb_tilt[n])) / 2000;
		foundstar_p_ray[n]        = (double) RANDOM (foundstar_p_orb_seed[n]) * 0.001 + 0.01;
		foundstar_p_ring[n]	 = zrandom (foundstar_p_ray[n]) * (1 + (double) RANDOM (1000) / 100);
		if (foundstar_class != 8)
			foundstar_p_type[n] = RANDOM (planet_types);
		else {
			if (RANDOM(2)) {
				foundstar_p_type[n] = 10;
				foundstar_p_orb_tilt[n] *= 100;
			}
			else
				foundstar_p_type[n] = RANDOM (planet_types);
		}
		if (foundstar_class==2||foundstar_class==7||foundstar_class==15)
			foundstar_p_orb_seed[n] *= 10;
	}

	if (!foundstar_class) {
		if (RANDOM(4)==2) foundstar_p_type[2] = 3;
		if (RANDOM(4)==2) foundstar_p_type[3] = 3;
		if (RANDOM(4)==2) foundstar_p_type[4] = 3;
	}

	for (n=0; n<foundstar_nop; n++) {
		switch (foundstar_class) {
			case 2: while (foundstar_p_type[n]==3)
					foundstar_p_type[n] = RANDOM (10);
				break;
			case 5: while (foundstar_p_type[n]==6||
				       foundstar_p_type[n]==9)
					foundstar_p_type[n] = RANDOM (10);
				break;
			case 7:	foundstar_p_type[n] = 9;
				break;
			case 9: while (foundstar_p_type[n]!=0&&
				       foundstar_p_type[n]!=6&&
				       foundstar_p_type[n]!=9)
					foundstar_p_type[n] = RANDOM (10);
				break;
			case 11:while (foundstar_p_type[n]!=1&&
				       foundstar_p_type[n]!=7)
					foundstar_p_type[n] = RANDOM (10);
		}
	}

	for (n=0; n<foundstar_nop; n++) {
		switch (foundstar_p_type[n]) {
			case 0:
				if (RANDOM(8))
					foundstar_p_type[n] ++;
				break;
			case 3:
				if ((n<2)||(n>6)||(foundstar_class&&RANDOM(4))) {
					if (RANDOM(2))
						foundstar_p_type[n]++;
					else
						foundstar_p_type[n]--;
				}
				break;
			case 7:
				if (n<7) {
					if (RANDOM(2))
						foundstar_p_type[n] --;
					else
						foundstar_p_type[n] -= 2;
				}
				break;
		}
	}

	foundstar_nob = foundstar_nop;

	if (foundstar_class==2||foundstar_class==7||foundstar_class==15)
		goto no_moons;

	for (n=0; n<foundstar_nop; n++) {
		// (t=) Numero di satelliti per pianeta.
		s = foundstar_p_type[n];
		if (n < 2) {
			t = 0;
			if (s == 10)
				t = RANDOM (3);
		}
		else
			t = RANDOM (planet_possiblemoons[s] + 1);
		if (foundstar_nob + t > maxbodies)
			t = maxbodies - foundstar_nob;
		for (c=0; c<t; c++) {
			q 			 = foundstar_nob + c;
			foundstar_p_owner[q]	 = n;
			foundstar_p_moonid[q]	 = c;
			foundstar_p_orb_orient[q] = (double) deg * (double) RANDOM (360);
			foundstar_p_orb_seed[q]   = (c*c+4) * foundstar_p_ray[n] + (float) zrandom (300 * foundstar_p_ray[n]) / 100;
			foundstar_p_tilt[q]       = zrandom (10*foundstar_p_orb_seed[q]) / 50;
			foundstar_p_orb_tilt[q]   = zrandom (10*foundstar_p_orb_seed[q]) / 500;
			foundstar_p_orb_ecc[q]    = 1 - (double) RANDOM (foundstar_p_orb_seed[q] + 10*fabs(foundstar_p_orb_tilt[q])) / 2000;
			foundstar_p_ray[q]        = (double) RANDOM (foundstar_p_orb_seed[n]) * 0.05 + 0.1;
			foundstar_p_ring[q]	 = 0;
			foundstar_p_type[q]       = RANDOM (planet_types);
			r = foundstar_p_type[q];
			if (r==9 && s != 10) r = 2;
			if (r==6 && s < 9) r = 5;
			if (n > 7 && RANDOM(c)) r = 7;
			if (n > 9 && RANDOM(c)) r = 7;
			if (r==2 || r==3 || r==4 || r==8) {
				if (s != 6 && s < 9)
					r = 1;
			}
			if (r==3 && s < 9) {
				if (n > 7)
					r = 7;
				if (foundstar_class && RANDOM(4))
					r = 5;
				if (foundstar_class == 2 ||
				    foundstar_class == 7 ||
				    foundstar_class == 11)
					r = 8;
			}
			if (r==7 && n <= 5) r = 1;
			if ((foundstar_class==2||foundstar_class==5||
			     foundstar_class==7||foundstar_class==11)
			     && RANDOM(n)) r = 7;
			foundstar_p_type[q] = r;
		}
		foundstar_nob += t;
	}

no_moons:
	key_radius = foundstar_ray * planet_orb_scaling;
	if (foundstar_class == 8) key_radius *= 2;
	if (foundstar_class == 2) key_radius *= 16;
	if (foundstar_class == 7) key_radius *= 18;
	if (foundstar_class == 11) key_radius *= 20;
	for (n=0; n<foundstar_nop; n++) {
		foundstar_p_ray[n] = avg_planet_ray[foundstar_p_type[n]]
				  + avg_planet_ray[foundstar_p_type[n]] * zrandom (100) / 200;
		foundstar_p_ray[n] *= avg_planet_sizing;
		foundstar_p_orb_ray[n] = key_radius + key_radius * zrandom (100) / 500;
		foundstar_p_orb_ray[n] += key_radius * avg_planet_ray[foundstar_p_type[n]];
		if (n < 8)
			key_radius += foundstar_p_orb_ray[n];
		else
			key_radius += 0.22 * foundstar_p_orb_ray[n];
	}

	n = foundstar_nop;
	while (n < foundstar_nob) {
		q = 0;
		c = foundstar_p_owner[n];
		key_radius = foundstar_p_ray[c] * moon_orb_scaling;
		while (n<foundstar_nob && foundstar_p_owner[n] == c) {
			foundstar_p_ray[n] = avg_planet_ray[foundstar_p_type[n]]
					  + avg_planet_ray[foundstar_p_type[n]] * zrandom (100) / 200;
			foundstar_p_ray[n] *= avg_moon_sizing;
			foundstar_p_orb_ray[n] = key_radius + key_radius * zrandom (100) / 250;
			foundstar_p_orb_ray[n] += key_radius * avg_planet_ray[foundstar_p_type[n]];
			if (q < 2) key_radius += foundstar_p_orb_ray[n];
			if (q >= 2 && q < 8) key_radius += 0.12 * foundstar_p_orb_ray[n];
			if (q >= 8) key_radius += 0.025 * foundstar_p_orb_ray[n];
			q++;
			n++;
		}
	}

	for (n = 0; n < foundstar_nop; n++) {
		foundstar_p_ring[n] = 0.75 * foundstar_p_ray[n] * (2 + RANDOM(3));
		s = foundstar_p_type[n];
		if (s != 6 && s != 9) {
			if (RANDOM(5))
				foundstar_p_ring[n] = 0;
		}
		else {
			if (RANDOM(2))
				foundstar_p_ring[n] = 0;
		}
	}

	
	for (n = 0; n < foundstar_nob; n++)
		foundstar_p_rtperiod[n] = 0;
}
// ---------------------------------------------------------------------------------

Dword flat_rnd_seed;

void fast_srand (Dword seed) // selezione tabella pseudo.
			    // 4.294.967.295 tabelle possibili.
			    // (ca. 20000 elementi diversi per tabella)
{
	asm {
		or word ptr seed, 3
		#ifdef WINDOWS
		mov eax, dword ptr seed
		mov dword ptr flat_rnd_seed, eax
		#else
		db 0x66; mov ax, word ptr seed
		db 0x66; mov word ptr flat_rnd_seed, ax
		#endif
	}
}

Dword fast_random (Dword mask) // estrazione di un numero: "mask" attiva i bits.
{
	Dword num;

	asm {
		#ifdef WINDOWS
		mov eax, dword ptr flat_rnd_seed
		mov edx, dword ptr flat_rnd_seed
		mul edx
			 add al, dl
		add dword ptr flat_rnd_seed, eax
		and eax, dword ptr mask
		mov dword ptr num, eax
		#else
		db 0x66; mov ax, word ptr flat_rnd_seed
		db 0x66; mov dx, word ptr flat_rnd_seed
		db 0x66; mul dx
			 add al, dl
		db 0x66; add word ptr flat_rnd_seed, ax
		db 0x66; and ax, word ptr mask
		db 0x66; mov word ptr num, ax
		#endif
	}

	return (num);
}

Word ranged_fast_random (Word range)
{
	if (range<=0) range = 1;
	return (fast_random(0x7FFF) % range);
}

float flandom ()
{ return ((float)RANDOM(32767) * 0.000030518); }

float fast_flandom ()
{ return ((float)fast_random(32767) * 0.000030518); }
