/*

	Inbox32.

*/

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

#pragma pack(2)
struct Guide1Entry {
	double subject;
	char entry[76];
};
#pragma pack()

Starmap1Entry *starmap1 = NULL;
Starmap1Entry *starmap1new = NULL;
Starmap1Entry *starmap1originals = NULL;
Starmap1Entry *search1Key = NULL;
Guide1Entry *guide1 = NULL;
Guide1Entry *guide1originals = NULL;

long long num1Entries;
long long max1Entries;
long long numG1Entries;
long long maxG1Entries;
long long numGO1Entries;
long long maxGO1Entries;
long long numO1Entries;
long long maxO1Entries;
long long numN1Entries;
long long maxN1Entries;

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

void main() {
	CHECK_SITUATION;
	FILE * starmap1File = fopen("data\\starmap.bin", "rb");
	if (starmap1File==NULL) {
		printf("Missing data\\starmap.bin.\n");
		printf("Press any key to continue.");
		getch();
		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 = (prevLen-4)>>5;
	max1Entries = num1Entries+1024;
	starmap1 = new Starmap1Entry[max1Entries];
	fread(starmap1, 32, num1Entries, starmap1File);
	qsort(starmap1, num1Entries, 32,
		(int (__CLIB *)
		(const void *,const void *))CompareSM1E);
	
	numO1Entries = ((starmap1Len-prevLen))>>5;
	maxO1Entries = numO1Entries;
	starmap1originals = new Starmap1Entry[numO1Entries];
	fread(starmap1originals, 32, numO1Entries, starmap1File);
	qsort(starmap1originals, numO1Entries, 32,
		(int (__CLIB *)
		(const void *,const void *))CompareSM1E);
	
	numN1Entries = 0;
	maxN1Entries = 1024;
	starmap1new = new Starmap1Entry[maxN1Entries];
	
	search1Key = new Starmap1Entry[1];
	fclose(starmap1File); starmap1File=NULL;
	
	FILE * guide1File = fopen("data\\guide.bin", "rb");
	if (guide1File==NULL) {
		printf("Missing data\\guide.bin.\n");
		printf("Press any key to continue.");
		getch();
		return;
	}
	long guide1Len = 0;
	fseek(guide1File, 0, SEEK_END);
	guide1Len = ftell(guide1File);
	fseek(guide1File, 0, SEEK_SET);
	prevLen = 0;
	fread(&prevLen, 4, 1, guide1File);
	//printf("actual len is %li, prevLen is %li.\n", guide1Len, prevLen);
	numG1Entries = (prevLen-4)/84;
	maxG1Entries = numG1Entries+1024;
	guide1 = new Guide1Entry[maxG1Entries];
	fread(guide1, 84, numG1Entries, guide1File);
	
	numGO1Entries = (guide1Len-prevLen)/84;
	maxGO1Entries = numGO1Entries;
	guide1originals = new Guide1Entry[numGO1Entries];
	fread(guide1originals, 84, numGO1Entries, guide1File);
	
	fclose(guide1File); guide1File=NULL;
	
	//read inbox.zip
	char entry[8];
	char label[25];
	char gtxt[76];
	FILE* duplicatesFile = NULL;
	#ifdef MERGER
	FILE* resultFile = fopen("data\\INBOX.ZIP", "wb");
	FILE* resultFile2 = fopen("data\\INBOX.TMP", "wb");
	
	
	fwrite("STARMAP_", 8, 1, resultFile);
	fwrite("GUIDE___", 8, 1, resultFile2);
	for (int mergeId=1; ; mergeId++) {
		char inboxFilename[20];
		sprintf(inboxFilename, "data\\OUTBOX.%03i", mergeId);
		FILE * inboxFile = fopen(inboxFilename, "rb");
		if (inboxFile==NULL) {
			printf("%i inboxes merged.\n", mergeId-1);
			break;
	#else
	FILE * inboxFile = fopen("data\\INBOX.ZIP", "rb");
	if (inboxFile==NULL) {
		printf("data\\inbox.zip not found.\n");
	#endif
	} else {
		int inGuide=0;
		int brokenInbox=0;
		int len = fread(entry, 8, 1, inboxFile);
		if (len!=1) {
			printf("It looks like inbox.zip is incomplete. (Missing header)\n");
			brokenInbox=1;
		} else {
			if (memcmp (entry, "STARMAP_", 8)) {
				printf("It looks like inbox.zip is invalid or corrupt. (Header doesn't say 'STARMAP_')\n");
			} else {
				printf("Importing starmap entries.\n");
				int cont=1;
				while (cont) {
					len = fread(entry, 8, 1, inboxFile);
					if (len!=1) {
						brokenInbox=1;
						printf("It looks like inbox.zip is incomplete. (Missing entry)\n");
						break;
					}
					
					if (!memcmp (entry, "GUIDE___", 8)) {
						printf("Importing guide entries.\n");
						inGuide=1;
						break;
					}
					len = fread(label, 24, 1, inboxFile);
					if (len!=1) {
						brokenInbox=1;
						printf("It looks like inbox.zip is incomplete. (Missing label)\n");
						break;
					}
					memcpy(&(search1Key->id), entry, 8);
					
					//Check if the entry is one that we had that wasn't in the last inbox we got. If so, then we no longer need to keep it separate.
					Starmap1Entry *result = (Starmap1Entry*) bsearch(search1Key, starmap1originals, numO1Entries, 32,
						(int (__CLIB *)
						(const void *,const void *))CompareSM1EApprox);
					//We erase it from 'originals', since it's now in normal.
					if (result!=NULL) {
						//This is our sign that it's no longer needed.
						if (result->name[0]==0) {
							//This entry is in the inbox twice!
						} else {
							if (memcmp(result->name, label, 24)!=0) {
								printf("'%.24s' has been replaced by '%.24s'.\n", result->name, label);
							}
							result->name[0]=0;
							result=NULL;
						}
					}
					
					if (result==NULL) {
							
						result = (Starmap1Entry*) bsearch(search1Key, starmap1, num1Entries, 32,
							(int (__CLIB *)
							(const void *,const void *))CompareSM1EApprox);
						
						if (result==NULL) {
							/*memcpy(&(search1Key->id), "Removed:", 8);
							result = (Starmap1Entry*) bsearch(search1Key, starmap1, num1Entries, 32,
								(int (__CLIB *)
								(const void *,const void *))CompareSM1E);
							if (result!=NULL) {
								//replace it
								memcpy(&(result->id), entry, 8);
								memcpy(result->name, label, 24);
							} else {*/
								//add a new one
							if (numN1Entries>=maxN1Entries) {
								Starmap1Entry *newEntries = new Starmap1Entry[maxN1Entries+1024];
								memcpy(newEntries, starmap1new, maxN1Entries*32);
								delete [] starmap1new;
								starmap1new = newEntries;
								maxN1Entries+=1024;
							}
							memcpy(&(starmap1new[numN1Entries].id), entry, 8);
							memcpy(starmap1new[numN1Entries].name, label, 24);
							numN1Entries++;
							#ifdef MERGER
							fwrite(entry, 8, 1, resultFile);
							fwrite(label, 24, 1, resultFile);
							#endif
							
							//Re-sort it.
							/*qsort(starmap1new, numN1Entries, 32,
								(int (__CLIB *)
								(const void *,const void *))CompareSM1E);
							*/
							//}
							
						} else {
							//it already exists, ignore it
							memcpy(gtxt, result->name, 24);
							if (duplicatesFile==NULL) {
								duplicatesFile = fopen("duplicates.err", "wt");
							}
							if (memcmp(gtxt, label, 24)!=0) {
								#ifdef MERGER
								fprintf(duplicatesFile, "Duplicate starmap entry '%.24s' (%s) already exists as '%.24s'- Keeping '%.24s'.\n", label, inboxFilename, gtxt, gtxt);
								#else
								fprintf(duplicatesFile, "Duplicate starmap entry '%.24s' already exists as '%.24s'- Keeping '%.24s'.\n", label, gtxt, gtxt);
								#endif
								
							}
						}
					}
				}
				while (inGuide) {
					len = fread(entry, 8, 1, inboxFile);
					if (len!=1) {
						if (len<=0) {
							inGuide=0;
						} else {
							brokenInbox=1;
							printf("It looks like inbox.zip is missing part of the guide section.\n");
						}
						break;
					}
					len = fread(gtxt, 76, 1, inboxFile);
					if (len!=1) {
						brokenInbox=1;
						printf("It looks like inbox.zip is missing part of the guide section.\n");
						break;
					}
					
					int found=0;
					//int firstRemoved=-1;
					for (int gnum=0; gnum<numGO1Entries; gnum++) {
						if (memcmp(&(guide1originals[gnum].subject),entry,8)==0) {
							if (memcmp(guide1originals[gnum].entry,gtxt,76)==0) {
								//This is our sign that it's no longer needed.
								guide1originals[gnum].entry[0]=0;
							}
						}
						
					}
					for (int gnum=0; gnum<numG1Entries; gnum++) {
						if (memcmp(&(guide1[gnum].subject),entry,8)==0) {
							if (memcmp(guide1[gnum].entry,gtxt,76)==0) {
								found=1;
								//if (firstRemoved!=-1) break;
								break;	//instead of the preceding
							}
						}
						/*if (memcmp(&(guide1[gnum].subject),"Removed:",8)==0) {
							if (firstRemoved==-1) {
								firstRemoved=gnum;
								if (found==1) break;
							}
						}*/
					}
					if (!found) {
						/*if (firstRemoved!=-1) {
							//replace it
							memcpy(&(guide1[firstRemoved]).subject, entry, 8);
							memcpy(guide1[firstRemoved].entry, gtxt, 76);
						} else {
						*/
						//add a new one
						if (numG1Entries>=maxG1Entries) {
							Guide1Entry *newEntries = new Guide1Entry[maxG1Entries+1024];
							memcpy(newEntries, guide1, maxG1Entries*84);
							delete [] guide1;
							guide1 = newEntries;
							maxG1Entries+=1024;
						}
						memcpy(&(guide1[numG1Entries].subject), entry, 8);
						memcpy(guide1[numG1Entries].entry, gtxt, 76);
						numG1Entries++;
						//}
						#ifdef MERGER
						fwrite(entry, 8, 1, resultFile2);
						fwrite(gtxt, 76, 1, resultFile2);
						#endif
					}
				}
			}
		}
		if (inboxFile!=NULL) {
			fclose(inboxFile); inboxFile=NULL;
		}
		//Write out files now
		if (!brokenInbox) {
			printf("Merging new and existing starmaps.\n");
			for (int idx=0; idx<numN1Entries; idx++) {
				/*memcpy(&(search1Key->id), "Removed:", 8);
				Starmap1Entry *result = (Starmap1Entry*) bsearch(search1Key, starmap1, num1Entries, 32,
					(int (__CLIB *)
					(const void *,const void *))CompareSM1E);
				if (result!=NULL) {
					//replace it
					memcpy(&(result->id), &starmap1new[idx].id, 8);
					memcpy(result->name, starmap1new[idx].name, 24);
				} else {
				*/
				if (num1Entries>=max1Entries) {
					Starmap1Entry *newEntries = new Starmap1Entry[max1Entries+1024];
					memcpy(newEntries, starmap1, max1Entries*32);
					delete [] starmap1;
					starmap1 = newEntries;
					max1Entries+=1024;
				}
				memcpy(&(starmap1[num1Entries].id), &starmap1new[idx].id, 8);
				memcpy(starmap1[num1Entries].name, starmap1new[idx].name, 24);
				num1Entries++;
				//}
			}
			numN1Entries=0;
			//Re-sort it.
			qsort(starmap1, num1Entries, 32,
				(int (__CLIB *)
				(const void *,const void *))CompareSM1E);
			printf("Saving starmap.\n");
			FILE * outStarmap1 = fopen("data\\starmap.bin", "wb");
			int size = num1Entries*32+4;
			fwrite(&size, 4, 1, outStarmap1);
			fwrite(starmap1, 32, num1Entries, outStarmap1);
			for (int idx=0; idx<numO1Entries; idx++) {
				if (starmap1originals[idx].name[0]!=0 && memcmp(&starmap1originals[idx].id,"Removed:",8)!=0) {
					fwrite(&(starmap1originals[idx]), 32, 1, outStarmap1);
				}
			}
			fclose(outStarmap1);
			
			printf("Saving guide.\n");
			FILE * outGuide1 = fopen("data\\guide.bin", "wb");
			size = numG1Entries*84+4;
			fwrite(&size, 4, 1, outGuide1);
			fwrite(guide1, 84, numG1Entries, outGuide1);
			for (int idx=0; idx<numGO1Entries; idx++) {
				if (guide1originals[idx].entry[0]!=0 && memcmp(&guide1originals[idx].subject,"Removed:",8)!=0) {
					fwrite(&(guide1originals[idx]), 84, 1, outGuide1);
				}
			}
			fclose(outGuide1);
		}
	}
	#ifdef MERGER
	}
	fclose(resultFile2);
	resultFile2=fopen("data\\INBOX.TMP", "rb");
	if (resultFile2==NULL) {
		printf("Unexpected error: Unable to open temporary file inbox.tmp for reading!\n");
	} else {
		while (1) {
			int len = fread(gtxt, 1, 76, resultFile2);
			if (len>0) {
				fwrite(gtxt, len, 1, resultFile);
			} else {
				break;
			}
		}
		fclose(resultFile2);
	}
	
	if (resultFile!=NULL) {
		fclose(resultFile); resultFile=NULL;
	}
	#endif
	if (duplicatesFile!=NULL) {
		fclose(duplicatesFile); duplicatesFile=NULL;
	}
	delete [] search1Key;
	delete [] starmap1;
	delete [] starmap1originals;
	delete [] guide1;
	printf("Done. Press any key to continue.");
	getch();
}