// SUBLEQ C compiler backend, made by Geri
// 64 bit big endian/unaligned access
// Self-hosting binary, Dawn compatible
// This file is under BSD license.

// To compile this file, use:
// g++ cc.c -o cc -O3 -s

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int n, char ** args){
	char * mem=NULL;
	
	// checking input
	if(n<2){printf("Error: no input file specified!\n");exit(0);}
	if(!args[1]){printf("Error: no input file specified!\n");exit(0);}
	
	// loading file
	char * ff=args[1];
	FILE *f=fopen(ff, "rb");
	if(!f){printf("Error: input file not found: %s\n", args[1]);exit(0);}
	fseek(f, 0, SEEK_END);
	long fsize=ftell(f);
	fseek(f, 0, SEEK_SET);
	long long mem_hint=(512*1024*1024)+fsize*1024; // allocating enough memory to fit the end result for sure
	mem=(char*)malloc(mem_hint);
	memset(mem, 0, mem_hint);
	
	// loading the file at the end of this memory area
	long long file_address=(mem_hint-fsize)-24;
	fread(&mem[file_address], 1, fsize, f);
	fclose(f);
	
	// creating output
	char outfg[256];
	snprintf(outfg, 250, "%s.run", ff);
	remove(outfg);
	FILE *of=fopen(outfg, "wb");
	if(!of){printf("Error creating output file: %s\n", outfg);exit(0);};
	
	
	// parsing includes. 
	int includewarning=0;
	while(char * inclfile=strstr(&mem[file_address], "#include")){
		if(!includewarning){
			includewarning++;
			printf("warning: includes are experimental in this backend.\n");
		}
		int j=7;
		int elsokapocs=0;
		int masodikkapocs=0;
		while(1){
			j++;
			if(inclfile[j]==0){printf("error parsing include: %s\n", inclfile);exit(0);};
			if(inclfile[j]=='<') break; // no system level includes available
			if(inclfile[j]=='\"'){
				if(!elsokapocs) elsokapocs=j+1; else{masodikkapocs=j;break;}
			}
		}
		if(masodikkapocs){
			inclfile[masodikkapocs]=0;
			FILE *f=fopen(&inclfile[elsokapocs], "rb");
			if(!f){printf("Error: input file not found: %s\n", &inclfile[elsokapocs]);exit(0);}
			fseek(f, 0, SEEK_END);
			long fsize2=ftell(f);
			fseek(f, 0, SEEK_SET);
			memset(inclfile, ' ', masodikkapocs+1);
			long long whely=(long long)inclfile-(long long)mem;
			mem=(char*)realloc(mem, mem_hint+fsize2+24);
			memmove(&mem[whely+fsize2], &mem[whely], 1+mem_hint-whely);
			mem_hint+=fsize2;
			fread(&mem[whely], 1, fsize2, f);
			fclose(f);
		}
	}
	
	// loading Dawn image
	f=fopen("disk0.bin", "rb");
	if(!f){f=fopen("program.bin", "rb");}
	if(!f)f=fopen("../disk0.bin", "rb");
	if(!f){f=fopen("../program.bin", "rb");}
	if(!f)f=fopen("../../disk0.bin", "rb");
	if(!f){f=fopen("../../program.bin", "rb");}
	// maybe the dawn image is in the compilers directory?
	if(!f){
		#ifdef __linux__
			int pidem=getpid();
			char gpidd[1024];snprintf(gpidd, 900, "readlink /proc/%d/exe > /tmp/%d_PID.txt", pidem,pidem);
			system(gpidd);
			snprintf(gpidd, 920, "/tmp/%d_PID.txt",pidem);
			FILE * pidkolvas=NULL;pidkolvas=fopen(gpidd, "r");
			if(!pidkolvas){
			}else{
				char eleresiut[8000];
				if(fgets(eleresiut, 7998, pidkolvas)==NULL){
				}else{
					char eleresiut2[8050];
					snprintf(eleresiut2, 8048, "%s", eleresiut);
					for(int vegetorol=strlen(eleresiut2);(vegetorol>1) && !(eleresiut2[vegetorol]=='/');vegetorol--) eleresiut2[vegetorol]='\0';
					char eleresiut2LINUX[8050];
					snprintf(eleresiut2LINUX, 8048, "%s", eleresiut2);
					chdir(eleresiut2);
				}
				fclose(pidkolvas);
			}
			remove(gpidd);
		#endif
	}
	if(!f)f=fopen("disk0.bin", "rb");
	if(!f){f=fopen("program.bin", "rb");}
	if(!f)f=fopen("../disk0.bin", "rb");
	if(!f){f=fopen("../program.bin", "rb");}
	if(!f)f=fopen("../../disk0.bin", "rb");
	if(!f){f=fopen("../../program.bin", "rb");}
	if(!f){f=fopen("/media/d/Relases/Dawn/disk0.bin", "rb");}
	if(!f){printf("Error: Dawn operating system binary not found.\nYou must copy the Dawn boot disk (disk0.bin) to the current directory, or to the directory of your compiler binary.\n");fclose(of);remove(outfg);exit(0);}

	fread(mem, 1, 256*1024*1024, f);
	fclose(f);

	// Preparing Dawn:
	unsigned long long * RAMW=(unsigned long long*)&mem[64];
	RAMW[0]=__bswap_64(56789); // magic flag for compiler mode
	RAMW[1]=__bswap_64(file_address); // the C file.
	
	// Executing and running the compiler.
	printf("Starting...");fflush(stdout);
	int eip=0;
	while(1){
		for(int i=0;i<512;i++){ // running dawn for a while
			unsigned long long * utasitaskezd=(unsigned long long*)&mem[eip];
			long long A=__bswap_64(utasitaskezd[0]);
			long long B=__bswap_64(utasitaskezd[1]);
			long long B_ertek=__bswap_64(*(unsigned long long*)&mem[B])-__bswap_64(*(unsigned long long*)&mem[A]);
			*(long long*)&mem[B]=__bswap_64(B_ertek);
			if(B_ertek<=0){
				eip=__bswap_64(utasitaskezd[2]);
			}else{
				eip+=24;
			}
			
			utasitaskezd=(unsigned long long*)&mem[eip];
			A=__bswap_64(utasitaskezd[0]);
			B=__bswap_64(utasitaskezd[1]);
			B_ertek=__bswap_64(*(unsigned long long*)&mem[B])-__bswap_64(*(unsigned long long*)&mem[A]);
			*(long long*)&mem[B]=__bswap_64(B_ertek);
			if(B_ertek<=0){
				eip=__bswap_64(utasitaskezd[2]);
			}else{
				eip+=24;
			}
		}

		if(RAMW[0]){
			long long lr=__bswap_64(RAMW[0]);
			if(RAMW[1]){
				// showing errors
				if(lr==2){
					RAMW[0]=0;
					if(RAMW[1]){
						long long err_add=__bswap_64(RAMW[1])+(long long)mem;
						RAMW[1]=0;
						printf("\033[20D%s             ", (char*)err_add);
						if(!strstr((char*)err_add, "\n")) printf("\n");
					}
					if(RAMW[2]){
						long long err_add=__bswap_64(RAMW[2])+(long long)mem;
						RAMW[1]=0;
						printf("%s", (char*)err_add);
						if(!strstr((char*)err_add, "\n")) printf("\n");
					}
				}
				// showing process bar
				if(lr==1){
					RAMW[0]=0;
					long long pb=__bswap_64(RAMW[1])+(long long)mem;
					RAMW[1]=0;
					printf("\033[20DCompiling: %s", (char*)pb);fflush(stdout);
				}
			}
			// finished
			if(lr==3){
				if(RAMW[1]){
					long long bin=__bswap_64(RAMW[1])+(long long)mem;
					long long meret=__bswap_64(*(long long*)(bin+3*8));
					fwrite((char*)bin, 1, meret, of);
					fclose(of);
					printf("\033[20DSuccess.            \n%d KByte binary compiled.\n", meret/1024);
				}else{
					fclose(of);
					printf("Compiler terminated with error.\n");
					remove(outfg);
				}
				exit(0);
			}
		}
	}
	
	
}
