/*

	This is a starmap converter.
	
	
	-Shadowlord
*/

#include <stdlib.h>
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

//32 bytes
#pragma pack(1)
struct Starmap1Entry {
	double id;
	char name[20];
	char code[4];
};
#pragma pack()

//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)
	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()

Starmap1Entry *starmap1 = NULL;
Starmap1Entry *search1Key = NULL;
Starmap2Entry *search2Key = NULL;
Starmap2Entry *curEntry2 = NULL;
Starmap2Entry *starmap2 = NULL;
Starmap2Entry *starmap2b = NULL;

long long num1Entries;

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


static int __CLIB CompareSM1EApprox(const void* aa, const void* ab) {
	Starmap1Entry *a = ((Starmap1Entry*)aa);
	Starmap1Entry *b = ((Starmap1Entry*)ab);
	int retval = 0;
	if (fabs(a->id - b->id)<0.00001) {
		retval=0;
	} else if (a->id > b->id) {
		retval=1;
	} else {
		retval=-1;
	}
	return retval;
}

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

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
double laststar_id;
long laststar_x, laststar_y, laststar_z;
FILE *starmap2File;
long long copiedSystems=0;
long long numSystems=0;
long long numValidSystems=0;
long long copiedPlanets=0;

//#define ANALYZE

#ifdef ANALYZE
long long errors[0x80];
#endif

char Scan (short analyzed_sectors_range, double center_x, double center_y, double center_z) {
	#ifdef ANALYZE
	for (int a=0; a<0x80; a++) {
		errors[a]=0;
	}
	#endif
	long startTime = clock();
	
	unsigned short visible_sectors_x = analyzed_sectors_range;
	unsigned short visible_sectors_y = analyzed_sectors_range;
	unsigned short visible_sectors_z = analyzed_sectors_range;
	if (visible_sectors_y>(4E4/30.0)+2) {
		visible_sectors_y = (unsigned short)(4E4/30.0)+2;
	}
	printf ("Scanning %i*%i*%i\n", visible_sectors_x, visible_sectors_y, visible_sectors_z);
	long rarity_factor, xz, mixer;
	
	long   	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;
	long start_y = sect_y;
	long start_z = sect_z;
	#ifdef ANALYZE
	unsigned int errFlags = 0;
	#endif
	
	for (unsigned short sx = visible_sectors_x; sx>0; sect_y=start_y, sect_x+=advance, sx--) {
		double dsect_x = sect_x;
		//if (fabs(dsect_x)>2E9) continue;
		
		dsect_x*=dsect_x;
		for (unsigned short sy = visible_sectors_y; sy>0; sect_z=start_z, sect_y+=advance, sy--) {
			double dsect_y = sect_y; dsect_y=30*fabs(dsect_y);
			//if (fabs(dsect_y)>2E9) continue;
			
			for (unsigned short sz = visible_sectors_z; sz>0; sect_z+=advance, sz--) {
				double dsect_z = sect_z;
				//if (fabs(dsect_z)>2E9) continue;
				dsect_z*=dsect_z;
				double distance_from_home = sqrt (dsect_x + dsect_z) + dsect_y;
				#ifndef ANALYZE
				if (distance_from_home <= 2E9) {
				#endif
					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;
					#ifndef ANALYZE
					if (laststar_x!=0) {
					#endif
						laststar_y = ImulAndAdd(laststar_x, xz);
						mixer = xz + laststar_y;
						laststar_y = (laststar_y & 0x1ffff) + sect_y - halfadvance;
						#ifndef ANALYZE
						if (laststar_y!=0) {
						#endif
							laststar_z = ImulAndAdd(laststar_y, mixer);
							laststar_z = (laststar_z & 0x1ffff) + sect_z - halfadvance;
							#ifndef ANALYZE
							if (laststar_z!=0) {
							#endif
								short rarity = (((short)laststar_x) + ((short)laststar_y) + ((short)laststar_z));
								#ifndef ANALYZE
								if ((rarity & rarity_factor) == 0) {
								#endif
									laststar_id = ((double)laststar_x)/advance*((double)laststar_y)/advance*((double)laststar_z)/advance;
									search1Key->id=laststar_id;
									Starmap1Entry *result = (Starmap1Entry*) bsearch(search1Key, starmap1, num1Entries, 32,
										(int (__CLIB *)
										(const void *,const void *))CompareSM1E);
									numSystems++;
									if (result!=NULL) {
										#ifdef ANALYZE
										errFlags=0;
										errfl:
										if (distance_from_home > 2E9) {
											errFlags|=0x01;
										}
										if (laststar_x==0) {
											errFlags|=0x02;
										}
										if (laststar_y==0) {
											errFlags|=0x04;
										}
										if (laststar_z==0) {
											errFlags|=0x08;
										}
										if ((rarity & rarity_factor) != 0) {
											errFlags|=0x10;
										}
										
										
										if (errFlags!=0) {
											errors[errFlags]++;
										} else {
										#endif
										copySystem:
										copiedSystems++;
										numValidSystems+=1;
										curEntry2->star_x=laststar_x;
										curEntry2->star_y=laststar_y;
										curEntry2->star_z=laststar_z;
										curEntry2->index=0;
										curEntry2->unused=0;
										memcpy(curEntry2->name, result->name, 24);
										fwrite(curEntry2, 44, 1, starmap2File);
										#ifdef ANALYZE
										}
										#endif
										/*int debug=0;
										result->name[19]=0;
										if (strcmp(result->name,"BALASTRACKONASTREYA")==0) {
											debug=1;
										}*/
										long idx = ((long)result-(long)starmap1)>>5;
										long cidx = idx+1;
										int plnum=1;
										//if (debug) printf("idx=%li cidx=%li num1Entries=%li\n", idx, cidx, num1Entries);
										while (plnum<=80 && cidx>=0 && cidx<num1Entries) {
											//printf("cidx = %i", cidx);
											double id = starmap1[cidx].id;
											double iddiff = id-laststar_id;
											/*if (debug) {
												printf("idx=%li cidx=%li num1Entries=%li plnum=%li id=%f iddiff=%f\n", idx, cidx, num1Entries, plnum, id, iddiff);
												starmap1[cidx].name[19]=0;
												printf("Name %s.\n", starmap1[cidx].name);
											}*/
											if (iddiff<=0 || iddiff>80) {
												break;
											} else if ((int)iddiff!=iddiff) {
												
											} else {
												/*if (debug) {
													starmap1[cidx].name[19]=0;
													printf("Copied %s.\n", starmap1[cidx].name);
												}*/
												#ifdef ANALYZE
												if (errFlags!=0) {
													errors[0x40]++;
												} else {
												#endif
												copiedPlanets++;
												curEntry2->star_x=laststar_x;
												curEntry2->star_y=laststar_y;
												curEntry2->star_z=laststar_z;
												curEntry2->index=(int)iddiff;
												curEntry2->unused=(int)iddiff;
												memcpy(curEntry2->name, starmap1[cidx].name, 24);
												fwrite(curEntry2, 44, 1, starmap2File);
												plnum++;
												#ifdef ANALYZE
												}
												#endif
											}
											cidx++;
										}
										//debug=0;
										/*printf("Result is #%li.\n", (long) ((result-starmap1)));
										result->name[19]=0;
										printf("name is %s and id is %f.\n", result->name, result->id);*/
										
									} else {
										result = (Starmap1Entry*) bsearch(search1Key, starmap1, num1Entries, 32,
										(int (__CLIB *)
										(const void *,const void *))CompareSM1EApprox);
										if (result!=NULL) {
											#ifdef ANALYZE
											errFlags|=0x20;
											goto errfl;
											#else
											goto copySystem;
											#endif
										} else {
											#ifdef ANALYZE
											int errFlags2=0;
											if (distance_from_home > 2E9) {
												errFlags2|=0x01;
											}
											if (laststar_x==0) {
												errFlags2|=0x02;
											}
											if (laststar_y==0) {
												errFlags2|=0x04;
											}
											if (laststar_z==0) {
												errFlags2|=0x08;
											}
											if ((rarity & rarity_factor) != 0) {
												errFlags2|=0x10;
											}
											if (errFlags2==0) {
												numValidSystems+=1;
											}
											#endif
										}
										
									}
				#ifndef ANALYZE
								}
							}
						}
					}
				}
				#endif
			}
		}
		printf ("SLICE %d OF %d\n", visible_sectors_x - sx, visible_sectors_x);
		while (kbhit()) {
			if (getch() == 27) {
				return (0);
			}
		}
	}
	long endTime = clock();
	long timeDiff = endTime-startTime;
	printf("Time taken: %li seconds\n", timeDiff/1000);
	#ifdef ANALYZE
	long long totalErrors[7];
	long long totalErrs=0;
	for (int a=0; a<7; a++) {
		totalErrors[a]=0;
	}
	for (int a=1; a<0x80; a++) {
		long long errs = errors[a];
		printf("Invalid systems which are ");
		if (a&0x1) {
			printf("OUTOFRANGE ");
			totalErrors[0]+=errs;
		}
		if (a&0x2) {
			printf("X=0 ");
			totalErrors[1]+=errs;
		}
		if (a&0x4) {
			printf("Y=0 ");
			totalErrors[2]+=errs;
		}
		if (a&0x8) {
			printf("Z=0 ");
			totalErrors[3]+=errs;
		}
		if (a&0x10) {
			printf("NONEXISTANT-DUE-TO-RARITY ");
			totalErrors[4]+=errs;
		}
		if (a&0x20) {
			printf("NOTEXACT ");
			totalErrors[5]+=errs;
		}
		if (a&0x40) {
			printf("PLANET-IN-ILLEGAL-SYSTEM ");
			totalErrors[6]+=errs;
		}
		printf(": %lli\n", errs);
		totalErrs+=errs;
	}
	printf("Nonexistant for unknown reasons: %lli\n", (num1Entries-(copiedSystems+copiedPlanets))-totalErrs);
	printf("Total number of systems OUTOFRANGE: %lli\n", totalErrors[0]);
	printf("Total number of systems X=0: %lli\n", totalErrors[1]);
	printf("Total number of systems Y=0: %lli\n", totalErrors[2]);
	printf("Total number of systems Z=0: %lli\n", totalErrors[3]);
	printf("Total number of systems NONEXISTANT-DUE-TO-RARITY: %lli\n", totalErrors[4]);
	printf("Total number of systems NOTEXACT: %lli\n", totalErrors[5]);
	printf("Total number of planets IN-ILLEGAL-SYSTEM: %lli\n", totalErrors[6]);
	
	#endif
	return (0);
}

void main() {
	FILE * starmap1File = fopen("data\\starmap.bin", "rb");
	if (starmap1File==NULL) {
		printf("Starmap.bin is missing, so er, I can't do anything.\n");
		return;
	}	
	long starmap1Len = 0;
	fseek(starmap1File, 0, SEEK_END);
	starmap1Len = ftell(starmap1File);
	fseek(starmap1File, 0, SEEK_SET);
	
	long prevLen = 0;
	fread(&prevLen, 4, 1, starmap1File);
	printf("actual len is %li, prevLen is %li.\n", starmap1Len, prevLen);
	num1Entries = (starmap1Len-4)>>5;
	
	starmap1 = new Starmap1Entry[num1Entries];
	fread(starmap1, 32, num1Entries, starmap1File);
	qsort(starmap1, num1Entries, 32,
		(int (__CLIB *)
		(const void *,const void *))CompareSM1E);
	search1Key = new Starmap1Entry[1];
	search2Key = new Starmap2Entry[1];
	curEntry2 = new Starmap2Entry[1];
	//goto skippety;
	starmap2File = fopen("data\\starmap2.new", "wb");
	//starmap1[512].name[19]=0;
	//printf("name in 512th entry is %s and id is %f.\n", starmap1[512].name, starmap1[512].id);
	
	printf("Scanning.\n");
	Scan(40002, 0, 0, 0);
	
	printf("%lli mapped systems found, %lli mapped planets too (total %lli), and %lli mapped entries are in starmap.bin. %lli systems exist total.\n", copiedSystems, copiedPlanets, (copiedSystems+copiedPlanets), num1Entries, numSystems);
	#ifndef ANALYZE
	printf("%lli valid systems exist total.\n", numValidSystems);
	#endif
	fclose(starmap2File); starmap2File=NULL;
	skippety:
	fclose(starmap1File); starmap1File=NULL;
	
	FILE *starmap2File1 = fopen("data\\starmap2.bin", "rb");
	if (starmap2File1==NULL) {
		printf("Starmap2.bin is missing, so no comparisons can be made between it and starmap2.new.\n");
		
	} else {
		FILE *starmap2File2 = fopen("data\\starmap2.new", "rb");
		if (starmap2File2==NULL) {
			printf("Starmap2.new is missing, so no comparisons can be made between it and starmap2.bin.\n");
			return;
		}	
		long starmap2Len1 = 0;
		fseek(starmap2File1, 0, SEEK_END);
		starmap2Len1 = ftell(starmap2File1);
		fseek(starmap2File1, 0, SEEK_SET);
		long starmap2Len2 = 0;
		fseek(starmap2File2, 0, SEEK_END);
		starmap2Len2 = ftell(starmap2File2);
		fseek(starmap2File2, 0, SEEK_SET);
		long num2Entries1 = (starmap2Len1)/44;
		long num2Entries2 = (starmap2Len2)/44;
		
		starmap2 = new Starmap2Entry[num2Entries1];
		starmap2b = new Starmap2Entry[num2Entries2];
		fread(starmap2, 44, num2Entries1, starmap2File1);
		qsort(starmap2, num2Entries1, 44,
			(int (__CLIB *)
			(const void *,const void *))CompareSM2E);
		fread(starmap2b, 44, num2Entries2, starmap2File2);
		qsort(starmap2b, num2Entries2, 44,
			(int (__CLIB *)
			(const void *,const void *))CompareSM2E);
		
		long inBoth = 0, onlyInOld=0, onlyInNew=0, inNeither=0;
		FILE * neitherf = fopen("neither.txt", "wt");
		if (neitherf==NULL) {
			printf("Could not open neither.txt for writing.\n");
			printf("Press any key to continue.\n");
			getch();
			return;
		}
		FILE * missingInNewf = fopen("missingInNew.txt", "wt");
		if (missingInNewf==NULL) {
			printf("Could not open missingInNew.txt for writing.\n");
			printf("Press any key to continue.\n");
			getch();
			return;
		}
		for (int a=0; a<num1Entries; a++) {
			memcpy(search2Key->name, starmap1[a].name, 24);
			
			Starmap2Entry *result = (Starmap2Entry*) bsearch(search2Key, starmap2b, num2Entries2, 44,
				(int (__CLIB *)
				(const void *,const void *))CompareSM2E);
			if (result==NULL) {
				result = (Starmap2Entry*) bsearch(search2Key, starmap2, num2Entries1, 44,
				(int (__CLIB *)
				(const void *,const void *))CompareSM2E);
				static char namecode[25];
				memcpy(namecode, starmap1[a].name, 24);
				namecode[24]=0;
				if (result==NULL) {
					fprintf(neitherf, "Could not find entry '%s' with id %f in either starmap2.\n", namecode, starmap1[a].id);
					inNeither++;
				} else {
					fprintf(missingInNewf, "New map missing entry '%s' with id %f which is in other starmap2, with coords of %li, %li, %li.\n", namecode, starmap1[a].id, result->star_x, result->star_y, result->star_z);
					onlyInOld++;
				}
			} else {
				result = (Starmap2Entry*) bsearch(search2Key, starmap2, num2Entries1, 44,
				(int (__CLIB *)
				(const void *,const void *))CompareSM2E);
				static char namecode[25];
				memcpy(namecode, starmap1[a].name, 24);
				namecode[24]=0;
				if (result==NULL) {
					//printf("Old map missing entry '%s' with id %f.\n", namecode, starmap1[a].id);
					onlyInNew++;
				} else {
					inBoth++;
				}
			}
		}
		if (neitherf!=NULL) fclose(neitherf);
		if (missingInNewf!=NULL) fclose(missingInNewf);
		printf("Entries in both: %li\nEntries in neither: %li\nEntries in old only: %li\nEntries in new only: %li\n", inBoth, inNeither, onlyInOld, onlyInNew);
		fclose(starmap2File1); starmap2File1=NULL;
		fclose(starmap2File2); starmap2File2=NULL;
		delete [] starmap2;
		delete [] starmap2b;
	}
	delete[] search1Key;
	delete [] search2Key;
	delete[] curEntry2;
	
	//512th: NRALLIS QUADREX PRI and id is -2587131635.330566
	
	delete [] starmap1;
	
	printf("Press any key to continue.\n");
	getch();
	return;
	
	
	//FILE * starmap2 = fopen("data\\starmap2.bin", "wb");
	
}