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



int decimal_to_binary(char* p, int32_t n)
{
  int32_t c, d, t;

  t = 0;

  if (p == NULL)
    exit(EXIT_FAILURE);

  for (c = 31 ; c >= 0 ; c--)
  {
    d = n >> c;

    if (d & 1)
      *(p+t) = 1 + '0';
    else
      *(p+t) = 0 + '0';

    t++;
  }
  *(p+t) = '\n';
  *(p+t+1) = '\0';

  return 0;
}

int unsigned_decimal_to_binary(char* p, uint32_t n)
{
	int c;
  uint32_t  d, t;

  t = 0;

  if (p == NULL)
    exit(EXIT_FAILURE);

  for (c = 31 ; c >= 0 ; c--)
  {
    d = n >> c;

    if (d & 1)
      *(p+t) = 1 + '0';
    else
      *(p+t) = 0 + '0';

    t++;
  }
  *(p+t) = '\n';
  *(p+t+1) = '\0';

  return 0;
}

int short_decimal_to_binary(char* p, uint16_t n)
{
	int c;
  uint16_t  d, t;

  t = 0;

  if (p == NULL)
    exit(EXIT_FAILURE);

  for (c = 15 ; c >= 0 ; c--)
  {
    d = n >> c;

    if (d & 1)
      *(p+t) = 1 + '0';
    else
      *(p+t) = 0 + '0';

    t++;
  }
  *(p+t) = '\n';
  *(p+t+1) = '\0';

  return 0;
}

int main (int argc, char ** argv) {
	int i = 0;
	uint32_t src, oldsrc, write, oldwrite, alu, oldalu, mem, oldmem;
	uint16_t pc, oldpc;
	char* reg = "01010\n";
	FILE* output;
	FILE* input;
	char str[40];
	output = fopen("./testdata/output.txt", "w");
	input = fopen("./testdata/input.txt", "w"); 	
	srand(time(NULL));


	

	for (i = 1; i < 100; i++) { 
		fprintf(input,"%d\n", i);
		fprintf(output,"%d\n", i);
		fprintf(input, "0\n0\n"); // no stall no flush

		alu = (uint32_t) rand();
		mem = (uint32_t) rand();
		pc = (uint16_t) rand();
		write = (uint32_t) rand() % 5;
		src = (uint32_t) rand() % 3;
		
		fputs(reg, input);


		if (write == 0) {
			fputs("0\n", input);
			fputs("0\n", output);
		}
		else {
			fputs("1\n", input);
			fputs("1\n", output);
		}

		fputs(reg, output);

		switch (src) {
			case 0: fputs("WBS_ALU\n", input); 
				unsigned_decimal_to_binary(str, alu);
				fputs(str, output);
				break;
			case 1: fputs("WBS_MEM\n", input);
				unsigned_decimal_to_binary(str, mem);
				fputs(str, output);
				break;
			case 2: fputs("WBS_OPC\n", input);
				unsigned_decimal_to_binary(str,(uint32_t) pc + 4);
				fputs(str, output);
				break;
			default : fputs("WBS_ALU\n", input); 
				unsigned_decimal_to_binary(str, alu);
				fputs(str, output);
				break;
		}

		unsigned_decimal_to_binary(str, alu);
		fputs(str, input);

		unsigned_decimal_to_binary(str, mem);
		fputs(str, input);

		short_decimal_to_binary(str, pc);
		fputs(str, input);

		


		fputs("\n", output);
		fputs("\n", input);

	}

	

	oldalu = alu;
	oldmem = mem;
	oldpc = pc;
	oldwrite = pc;
	oldsrc = src;
	//stall case
	for (i = 0; i < 20; i++) {
		fprintf(input,"%d\n", 100);
		fprintf(output,"%d\n", 100);
		fprintf(input, "1\n0\n"); //stall
		alu = (uint32_t) rand();
		mem = (uint32_t) rand();
		pc = (uint16_t) rand();
		write = (uint32_t) rand() % 5;
		src = (uint32_t) rand() % 3;
		
		fputs("10110\n", input);


		if (write == 0) {
			fputs("0\n", input);
		}
		else {
			fputs("1\n", input);
		}

		if (oldwrite == 0) {
			fputs("0\n", output);
		}
		else {
			fputs("1\n", output);
		}

		fputs(reg, output);

		switch (src) {
			case 0: fputs("WBS_ALU\n", input); 
				break;
			case 1: fputs("WBS_MEM\n", input);
				break;
			case 2: fputs("WBS_OPC\n", input);
				break;
			default : fputs("WBS_ALU\n", input); 
				break;
		}

		switch (oldsrc) {
			case 0: 
				unsigned_decimal_to_binary(str, oldalu);
				fputs(str, output);
				break;
			case 1: 
				unsigned_decimal_to_binary(str, oldmem);
				fputs(str, output);
				break;
			case 2: 
				unsigned_decimal_to_binary(str,(uint32_t) oldpc + 4);
				fputs(str, output);
				break;
			default :
				unsigned_decimal_to_binary(str, oldalu);
				fputs(str, output);
				break;
		}

		unsigned_decimal_to_binary(str, alu);
		fputs(str, input);

		unsigned_decimal_to_binary(str, mem);
		fputs(str, input);

		short_decimal_to_binary(str, pc);
		fputs(str, input);

		fputs("\n", output);
		fputs("\n", input);
	}


	//flush case
	for (i = 0; i < 20; i++) {
		fprintf(input,"%d\n", 100);
		fprintf(output,"%d\n", 100);
		fprintf(input, "0\n1\n"); //flush
		alu = (uint32_t) rand();
		mem = (uint32_t) rand();
		pc = (uint16_t) rand();
		write = (uint32_t) rand() % 5;
		src = (uint32_t) rand() % 3;
		
		fputs("10110\n", input);


		if (write == 0) {
			fputs("0\n", input);
		}
		else {
			fputs("1\n", input);
		}


		fputs("0\n", output);
		

		fputs("10110\n", output);

		switch (src) {
			case 0: fputs("WBS_ALU\n", input); 
				unsigned_decimal_to_binary(str, alu);
				fputs(str, output);
				break;
			case 1: fputs("WBS_MEM\n", input);
				unsigned_decimal_to_binary(str, mem);
				fputs(str, output);
				break;
			case 2: fputs("WBS_OPC\n", input);
				unsigned_decimal_to_binary(str,(uint32_t) pc + 4);
				fputs(str, output);
				break;
			default : fputs("WBS_ALU\n", input); 
				unsigned_decimal_to_binary(str, alu);
				fputs(str, output);
				break;
		}

		unsigned_decimal_to_binary(str, alu);
		fputs(str, input);

		unsigned_decimal_to_binary(str, mem);
		fputs(str, input);

		short_decimal_to_binary(str, pc);
		fputs(str, input);

		if (i < 19) {
			fputs("\n", output);
			fputs("\n", input);
		}
	}
	



	fclose(output);
	fclose(input);
}
