/*

	This converts from starmap2 to starmap3.
	
	
	-Shadowlord
*/

#include <stdlib.h>
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#define WINDOWS
#include "defs.h"
#include "randalg.h"
#include "genalg.h"
#undef WINDOWS


// ---------------------------------------------------------------------------------


//44 bytes
#pragma pack(1)
struct Starmap2Entry {
	int star_x;
	int star_y;
	int star_z;
	int index;	//(0 is a star, higher values indicate a planet, only used by NoctisMapper to determine if this entry is for a star or planet)
				//For planets, Triceratops writes the body number + 1 (i.e. 1-80).
	int unused;	// or unused (0 for stars, unknown for planets, ignored by NoctisMapper)
	char name[20];
	char code[4];	//(4B) string " S##" or " P##" where ## is the star type, or the body number, without a null terminator.
};
#pragma pack()

#pragma pack(1)
struct Starmap3Info {
	Uchar flags;	//Includes 'Is this a star?'
	Uchar p_bodyNumber;	//For planets, its body #.
	Starmap3Info * p_parentStar;	//For planets, a pointer to its star.
	Udword filePosition;	//Its position in the file. We write all stars first, and set filePosition as we do. Then when we write the planets we link 'em.
	//Sector coordinates
	Dword sx;
	Dword sy;
	Dword sz;
	//ID/seed/etc
	double id;
	char name[20];
};
#pragma pack()

Starmap2Entry *starmap2 = NULL;
Starmap3Info *starmap3 = NULL;
Starmap3Info *search3Key = NULL;

long long num2Entries;
long long num3Entries;
long long max3Entries;


static int __CLIB CompareSM2E(const void* aa, const void* ab) {
	Starmap2Entry *a = ((Starmap2Entry*)aa);
	Starmap2Entry *b = ((Starmap2Entry*)ab);
	int retval = 0;
	static char namecode1[25];
	static char namecode2[25];
	memcpy(namecode1, a->name, 24);
	memcpy(namecode2, b->name, 24);
	namecode1[24]=0;
	namecode2[24]=0;
	return strcmp(namecode1, namecode2);
}

static int __CLIB CompareSM3E(const void* aa, const void* ab) {
	Starmap3Info *a = ((Starmap3Info*)aa);
	Starmap3Info *b = ((Starmap3Info*)ab);
	int retval = 0;
	if (a->id < b->id) {
		retval=-1;
	} else if (a->id > b->id) {
		retval=1;
	} else {
		if (a->p_bodyNumber < b->p_bodyNumber) {
			retval=-1;
		} else if (a->p_bodyNumber > b->p_bodyNumber) {
			retval=1;
		}
	}
	return retval;
}


char isthere2 (double star_id, Word analyzed_sectors_range, double center_x, double center_y, double center_z) {
	double laststar_id;
	Dword laststar_x, laststar_y, laststar_z;

	Uword visible_sectors_x = analyzed_sectors_range;
	Uword visible_sectors_y = analyzed_sectors_range;
	Uword visible_sectors_z = analyzed_sectors_range;
	Uword maxvisy = ((2E9 + fabs(center_y))/30.0)*2+2;
	if (visible_sectors_y>maxvisy) {
		visible_sectors_y = (Uword)maxvisy;
	}
	Dword   	sect_x, sect_y, sect_z;

	sect_x = (center_x - visible_sectors_x*halfadvance) / advance; sect_x *= advance;
	sect_y = (center_y - visible_sectors_y*halfadvance) / advance; sect_y *= advance;
	sect_z = (center_z - visible_sectors_z*halfadvance) / advance; sect_z *= advance;
	
	Dword start_y = sect_y;
	Dword start_z = sect_z;
	
	Dword rarity_factor;
	Dword xz, mixer;
	for (Uword sx = visible_sectors_x; sx>0; sect_y=start_y, sect_x+=advance, sx--) {
		double dsect_x = sect_x; dsect_x*=dsect_x;
		for (Uword sy = visible_sectors_y; sy>0; sect_y=start_y, sect_y+=advance, sy--) {
			double dsect_y = sect_y; dsect_y=30*fabs(dsect_y);
			for (Uword sz = visible_sectors_z; sz>0; sect_z+=advance, sz--) {
				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--;
				xz = sect_x + sect_z;
				laststar_x = (xz & 0x1ffff) + sect_x - halfadvance;
				if (laststar_x!=0) {
					laststar_y = ImulAndAdd(laststar_x, xz);
					mixer = xz + laststar_y;
					laststar_y = (laststar_y & 0x1ffff) + sect_y - halfadvance;
					if (laststar_y!=0) {
						laststar_z = ImulAndAdd(laststar_y, mixer);
						laststar_z = (laststar_z & 0x1ffff) + sect_z - halfadvance;
						if (laststar_z!=0) {
							Word rarity = (((Word)laststar_x) + ((Word)laststar_y) + ((Word)laststar_z));
							if ((rarity & rarity_factor) == 0) {
								//if (fabs(laststar_id-star_id)<0.00001) return 1;
								laststar_id = ((double)laststar_x)/advance*((double)laststar_y)/advance*((double)laststar_z)/advance;
								if (laststar_id==star_id) {
									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;
									
									return 1;
								}
							}
						}
					}
				}
			}
		}
	}
	return (0);
}
FILE *starmap2File;
FILE *starmap3File;
long long copiedSystems=0;
long long numSystems=0;
long long copiedPlanets=0;

void main() {
	FILE* log = fopen("tri23.log", "wt");
	if (log==NULL) {
		printf("Unable to open log file.");
		return;
	}
	search3Key = new Starmap3Info[1];
	FILE *starmap2File = fopen("data\\starmap2.bin", "rb");
	if (starmap2File==NULL) {
		printf("Starmap2.bin is missing, so no comparisons can be made between it and starmap2.new.\n");
		
	} else {
		long starmap2Len = 0;
		fseek(starmap2File, 0, SEEK_END);
		starmap2Len = ftell(starmap2File);
		fseek(starmap2File, 0, SEEK_SET);
		num2Entries = (starmap2Len)/44;
		
		starmap2 = new Starmap2Entry[num2Entries];
		fread(starmap2, 44, num2Entries, starmap2File);
		//qsort(starmap2, num2Entries, 44,
		//	(int (__CLIB *)
		//	(const void *,const void *))CompareSM2E);
		
		max3Entries = 1024;
		num3Entries = 0;
		
		Uword formatVer = 1;
		Uqword verstamp = 0;
		
		starmap3 = new Starmap3Info[max3Entries];
		FILE *starmap3File = fopen("data\\starmap3.map", "wb");
		if (starmap3File==NULL) {
			printf("Cannot write starmap3.bin!\n");
			return;
		}
		FILE *starmap3GDFile = fopen("data\\starmap3.gd", "wb");
		if (starmap3GDFile==NULL) {
			printf("Cannot write starmap3.gd!\n");
			return;
		} else {
			fwrite(&formatVer, 2, 1, starmap3GDFile);
			fwrite(&verstamp, 8, 1, starmap3GDFile);
			fclose(starmap3GDFile); starmap3GDFile=NULL;
		}
		FILE *starmap3USRFile = fopen("data\\starmap3.usr", "wb");
		if (starmap3USRFile==NULL) {
			printf("Cannot write starmap3.usr!\n");
			return;
		} else {
			fwrite(&formatVer, 2, 1, starmap3USRFile);
			fwrite(&verstamp, 8, 1, starmap3USRFile);
			
			fclose(starmap3USRFile); starmap3USRFile=NULL;
		}
		starmap3USRFile = fopen("data\\starmap3.svr", "wb");
		if (starmap3USRFile==NULL) {
			printf("Cannot write starmap3.svr!\n");
			return;
		} else {
			fwrite(&formatVer, 2, 1, starmap3USRFile);
			fwrite(&verstamp, 8, 1, starmap3USRFile);
			
			fclose(starmap3USRFile); starmap3USRFile=NULL;
		}
		fwrite(&formatVer, 2, 1, starmap3File);
		fwrite(&verstamp, 8, 1, starmap3File);
		Udword discoverer = 0xffffffff;
		fprintf(log, "Reading entries from starmap2.\n");
		for (int entryNum=0; entryNum<num2Entries; entryNum++) {
			if (num3Entries>=max3Entries) {
				Starmap3Info *newEntries = new Starmap3Info[max3Entries+1024];
				memcpy(newEntries, starmap3, max3Entries*sizeof(Starmap3Info));
				delete [] starmap3;
				starmap3 = newEntries;
				max3Entries+=1024;
			}
			Uchar flags = 0;
			double search_id = ((double)starmap2[entryNum].star_x)/advance*((double)starmap2[entryNum].star_y)/advance*((double)starmap2[entryNum].star_z)/advance;
			//star x = (0-131071) + sect_x - halfadvance //(50000)
			//star y = (0-131071) + sect_y - halfadvance;
			//star z = (0-131071) + sect_z - halfadvance;
			if (search_id==0) {
				fprintf(log, "Search ID 0: %.20s\n", starmap2[entryNum].name);
			} else {
				if (isthere2(search_id, 4, starmap2[entryNum].star_x, starmap2[entryNum].star_y, starmap2[entryNum].star_z)) {
					int entryNum3 = num3Entries;
					num3Entries++;
					if (starmap2[entryNum].index==0) {		//star
						starmap3[entryNum3].p_bodyNumber = 0;	//This is used when sorting.
						flags|=0x8;	//bit 3: star
						extract_foundstar_infos();
						//prepare_foundstar();
						if (foundstar_class == 6 && foundstar_ray > 4)
							flags|=0x2;	//has fuel	- Note: this ignores S05s, even though you *can* scope from them.
						//bit 0 (0x1) will be set if there are any *named* felisian planets around this star.
					} else {	//planet
						//no flags are set for planets, however, we record two vars.
						starmap3[entryNum3].p_bodyNumber = starmap2[entryNum].index;
						starmap3[entryNum3].p_parentStar = NULL;	//We set this later
					}
					starmap3[entryNum3].flags=flags;
					starmap3[entryNum3].filePosition=0;
					starmap3[entryNum3].sx=foundstar_sx;
					starmap3[entryNum3].sy=foundstar_sy;
					starmap3[entryNum3].sz=foundstar_sz;
					starmap3[entryNum3].id=search_id;
					memcpy(starmap3[entryNum3].name, starmap2[entryNum].name, 20);
					fprintf(log, "Copying [%.20s] to [%.20s], entry num %i. ID %f (%llx). Sector(%i, %i, %i) Pos(%f, %f, %f)\n", starmap2[entryNum].name, starmap3[entryNum3].name, entryNum3, starmap3[entryNum3].id, starmap3[entryNum3].id, foundstar_sx, foundstar_sy, foundstar_sz, foundstar_x, foundstar_y, foundstar_z);
					
				} else {
					fprintf(log, "Could not find: %f (%llx) at X=%i Y=%i Z=%i ('index' is %i)\n", search_id, search_id, starmap2[entryNum].star_x, starmap2[entryNum].star_y, starmap2[entryNum].star_z, starmap2[entryNum].index);
				}
			}
		}
		fprintf(log, "Checking entries.\n");
		for (int entryNum3=0; entryNum3<num3Entries; entryNum3++) {
			fprintf(log, "Entry %i: [%.20s], ID %f, flags %i. Sector coords: %i %i %i. Body number %i.\n", entryNum3, starmap3[entryNum3].name, starmap3[entryNum3].id, starmap3[entryNum3].flags, starmap3[entryNum3].sx, starmap3[entryNum3].sy, starmap3[entryNum3].sz, starmap3[entryNum3].p_bodyNumber);
		}
		fprintf(log, "Sorting.\n");
		//Sort it
		qsort(starmap3, num3Entries, sizeof(Starmap3Info),
			(int (__CLIB *)
			(const void *,const void *))CompareSM3E);
		fprintf(log, "\n\n\nChecking cataloged system seeds for duplicates (clones). What this means is, for every star in the starmap, we check to see how many times we found it in the galaxy, since it is mathematically possible for system seeds to be the same for separate system positions. This was thought to be rare, and while it does turn out to be so, it also turns out to happen more often for a particular subset of systems: Take a look at the seeds, sector coordinates, and actual coordinates of the duplicated systems, and compare to those of normal stars.\n");
		Dword numDuplicates=0;
		Dword numDuplicateGroups=0;
		for (int entryNum3=0; entryNum3<num3Entries; entryNum3++) {
			if (starmap3[entryNum3].flags!=0) {
				if (starmap3[entryNum3].sx==starmap3[entryNum3].sz && starmap3[entryNum3].sy==0) {
					PrepareFromSectorCoords(starmap3[entryNum3].sx, starmap3[entryNum3].sy, starmap3[entryNum3].sz);
					fprintf(log, "NOTABLE Entry %i: [%.20s], ID %f (%llx), flags %i. Sector coords: %i %i %i. Actual coords: %f %f %f. Body number %i.\n", entryNum3, starmap3[entryNum3].name, starmap3[entryNum3].id, starmap3[entryNum3].id, starmap3[entryNum3].flags, starmap3[entryNum3].sx, starmap3[entryNum3].sy, starmap3[entryNum3].sz, foundstar_x, foundstar_y, foundstar_z, starmap3[entryNum3].p_bodyNumber);
				}
				int count=1;
				Uchar p_bodyNumber = starmap3[entryNum3].p_bodyNumber;
				double id = starmap3[entryNum3].id;
				for (int entryNum3B=0; entryNum3B<entryNum3; entryNum3B++) {
					if (starmap3[entryNum3B].id==id && starmap3[entryNum3B].p_bodyNumber==p_bodyNumber) {
						count=0;
						PrepareFromSectorCoords(starmap3[entryNum3].sx, starmap3[entryNum3].sy, starmap3[entryNum3].sz);
						fprintf(log, "\tEntry %i: [%.20s], ID %f (%llx), flags %i. Sector coords: %i %i %i. Actual coords: %f %f %f. Body number %i.\n", entryNum3, starmap3[entryNum3].name, starmap3[entryNum3].id, starmap3[entryNum3].id, starmap3[entryNum3].flags, starmap3[entryNum3].sx, starmap3[entryNum3].sy, starmap3[entryNum3].sz, foundstar_x, foundstar_y, foundstar_z, starmap3[entryNum3].p_bodyNumber);
						break;
					}
				}
				if (count>0) {
					for (int entryNum3B=entryNum3+1; entryNum3B<num3Entries; entryNum3B++) {
						if (starmap3[entryNum3B].id==id && starmap3[entryNum3B].p_bodyNumber==p_bodyNumber) {
							count++;
						}
					}
					if (count>1) {
						PrepareFromSectorCoords(starmap3[entryNum3].sx, starmap3[entryNum3].sy, starmap3[entryNum3].sz);
						fprintf(log, "DUPLICATE: Entry [%.20s] (id %f (%llx)) exists %i times!\n", starmap3[entryNum3].name, starmap3[entryNum3].id, starmap3[entryNum3].id, count);
						fprintf(log, "\tEntry %i: [%.20s], ID %f (%llx), flags %i. Sector coords: %i %i %i. Actual coords: %f %f %f. Body number %i.\n", entryNum3, starmap3[entryNum3].name, starmap3[entryNum3].id, starmap3[entryNum3].id, starmap3[entryNum3].flags, starmap3[entryNum3].sx, starmap3[entryNum3].sy, starmap3[entryNum3].sz, foundstar_x, foundstar_y, foundstar_z, starmap3[entryNum3].p_bodyNumber);
						numDuplicates+=count;
						numDuplicateGroups++;
						//fprintf(log, "DUPLICATE: Entry [%.20s] exists %i times!\n", starmap3[entryNum3].name, count);
					}
				}
			}
		}
		fprintf(log, "There were %i duplicate-groups (The number of cataloged system seeds which exist more than once), and %i duplicate systems (The number of systems in the galaxy with the same seed as at least one other system, where that seed belongs to a cataloged star).\n\n\n", numDuplicateGroups, numDuplicates);
		fprintf(log, "Finding felisian planets.\n");
		//Find felisian planets, find their stars, and mark them with flag 0. Also mark parent stars.
		for (int entryNum3=0; entryNum3<num3Entries; entryNum3++) {
			if (!(starmap3[entryNum3].flags&0x8)) {
				//planet
				PrepareFromSectorCoords(starmap3[entryNum3].sx, starmap3[entryNum3].sy, starmap3[entryNum3].sz);
				extract_foundstar_infos();
				prepare_foundstar();
				//find the parent star
				search3Key->id = starmap3[entryNum3].id;
				search3Key->p_bodyNumber = 0;
				Starmap3Info *result = (Starmap3Info*) bsearch(search3Key, starmap3, num3Entries, sizeof(Starmap3Info),
					(int (__CLIB *)
					(const void *,const void *))CompareSM3E);
				if (result!=NULL) {
					fprintf(log, "Marked parent star for %.20s, %.20s. ID %f, bodyNumber %i\n", starmap3[entryNum3].name, result->name, starmap3[entryNum3].id, starmap3[entryNum3].p_bodyNumber);
					starmap3[entryNum3].p_parentStar=result;
					//also if it is felisian, flag the parent star.
					if (foundstar_p_type[starmap3[entryNum3].p_bodyNumber-1]==3) {
						fprintf(log, "Marked system with felisian planet: %.20s\n", result->name);
						result->flags|=0x1;	//flag 0: has named felisian planet
					}
				}
				//Uchar p_bodyNumber;	//For planets, its body #.
				//Udword p_parentStar;	//For planets, its star's index #.
				
			}
		}
		
		fprintf(log, "Writing stars to starmap3.\n");
		//Write stars to the file and record their filePosition
		for (int entryNum3=0; entryNum3<num3Entries; entryNum3++) {
			if (starmap3[entryNum3].flags&0x8) {
				fprintf(log, "Writing star %.20s.\n", starmap3[entryNum3].name);
				//star
				starmap3[entryNum3].filePosition = ftell(starmap3File);
				fwrite(&starmap3[entryNum3].flags, 1, 1, starmap3File);
				fwrite(&discoverer, 4, 1, starmap3File);
				fwrite(&starmap3[entryNum3].name, 20, 1, starmap3File);
				fwrite(&starmap3[entryNum3].sx, 4, 1, starmap3File);
				fwrite(&starmap3[entryNum3].sy, 4, 1, starmap3File);
				fwrite(&starmap3[entryNum3].sz, 4, 1, starmap3File);
				Uword data = 0;
				PrepareFromSectorCoords(starmap3[entryNum3].sx, starmap3[entryNum3].sy, starmap3[entryNum3].sz);
				extract_foundstar_infos();
				prepare_foundstar();
				//stellar class, number of planets, number of moons
				data = (foundstar_class&0xF) | ((foundstar_nop&0x1F)<<4) | (((foundstar_nob-foundstar_nop)&0x7F)<<9);
				fwrite(&data, 2, 1, starmap3File);
				Dword zero = 0;
				fwrite(&zero, 4, 1, starmap3File);	//uint file position of next alternate name, or 0 if there are none.
			}
		}
		fprintf(log, "Writing planets and moons to starmap3.\n");
		//Now write planets.
		for (int entryNum3=0; entryNum3<num3Entries; entryNum3++) {
			if (!(starmap3[entryNum3].flags&0x8)) {
				if (starmap3[entryNum3].p_parentStar!=NULL) {
					fprintf(log, "Writing planet/moon %.20s.\n", starmap3[entryNum3].name);
					starmap3[entryNum3].filePosition = ftell(starmap3File);
					fwrite(&starmap3[entryNum3].flags, 1, 1, starmap3File);
					fwrite(&discoverer, 4, 1, starmap3File);
					fwrite(starmap3[entryNum3].name, 20, 1, starmap3File);
					fwrite(&(starmap3[entryNum3].p_parentStar->filePosition), 4, 1, starmap3File);	//(4B) uint file position of star
					Uword data = 0;
					PrepareFromSectorCoords(starmap3[entryNum3].sx, starmap3[entryNum3].sy, starmap3[entryNum3].sz);
					extract_foundstar_infos();
					prepare_foundstar();
					int bodyNum = starmap3[entryNum3].p_bodyNumber-1;
					data = (foundstar_p_type[bodyNum]&0xF);
					if (foundstar_p_owner[bodyNum]==-1) {	//planet, not moon
						data |= ((bodyNum&0x1F)<<4) | (0x1F<<9);
					} else {
						data |= ((foundstar_p_owner[bodyNum]&0x1F)<<4) | ((foundstar_p_moonid[bodyNum]&0x1F)<<9);
					}
					fwrite(&data, 2, 1, starmap3File);
					Dword zero = 0;
					fwrite(&zero, 4, 1, starmap3File);	//uint file position of next alternate name, or 0 if there are none.
				} else {
					fprintf(log, "Not writing planet/moon %.20s.\n", starmap3[entryNum3].name);
				}
			}
		}
		fprintf(log, "Done writing.\n");
		fclose(log); log=NULL;
		fclose(starmap2File); starmap2File=NULL;
		fclose(starmap3File); starmap3File=NULL;
		delete [] starmap2;
		delete [] starmap3;
	}
	
	printf("Press any key to continue.\n");
	getch();
	return;
}