package lab02;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.Random;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class TestCPU {
	private CPU cpu;
	private Memory mem;
	private int[] memCpy;
	private Random rand;

	@BeforeEach
	void setUp() throws Exception {
		mem = new Memory(100);
		rand = new Random();
		cpu = new CPU(mem);
		memCpy = new int[100];
		for (int i=0;i<100;i++) {
			int r = rand.nextInt();
			mem.set(i, r);
			memCpy[i]=r;
		}
	}

	@Test
	void testCPU() { 
	    assertEquals(0,cpu.getAccumulator());	
	}

	@Test
	void testExecute() { 
		// Includes an assert for each operation
		cpu.execute("LOD", "IMM",21);
		assertEquals(21,cpu.getAccumulator(),"LOD failed");
		cpu.execute("ADD", "IMM", 3);
		assertEquals(24,cpu.getAccumulator(),"ADD failed");
		cpu.execute("SUB", "IMM", 12);
		assertEquals(12,cpu.getAccumulator(),"SUB failed");
		cpu.execute("MUL", "IMM", 3);
		assertEquals(36,cpu.getAccumulator(),"MUL failed");
		cpu.execute("DIV", "IMM", 4);
		assertEquals(9,cpu.getAccumulator(),"DIV failed");
		checkMem();
	}
	
	@Test
	void testMemory() {
		cpu.execute("LOD", "IMM", 19);
		assertEquals(19,cpu.getAccumulator(),"LOD setup failed");
		cpu.execute("STO", "DIR", 7);
		assertEquals(19,mem.get(7),"STO to location 7 failed");
		memCpy[7]=19;
		cpu.execute("ADD", "IMM", 4);
		assertEquals(23,cpu.getAccumulator(),"ADD after STO failed");
		cpu.execute("LOD", "DIR", 7);
		assertEquals(19,cpu.getAccumulator(),"LOD direct failed");
		checkMem();
	}

	@Test
	void testIllegals() {
		cpu.execute("LOD", "IMM", 6);
		assertEquals(6,cpu.getAccumulator(),"LOD setup failed");
		cpu.execute("LOD", "ERR", 27);
		assertEquals(6,cpu.getAccumulator(),"Failed to ignore invalid mode");
		cpu.execute("STO","DIR",3);
		assertEquals(6,mem.get(3),"STO to location 3 failed");
		memCpy[3]=6;
		cpu.execute("LOD", "IMM", 17);
		assertEquals(17,cpu.getAccumulator(),"LOD modify failed");
		cpu.execute("STO", "IMM", 3);
		assertEquals(6,mem.get(3),"Failed to ignore invalid STO mode of IMM");
		cpu.execute("LOD", "DIR", -342);
		assertEquals(-1,cpu.getAccumulator(),"Failed to catch negative addr");
		cpu.execute("STO", "DIR", 365);
		checkMem();
	}
	
	@Test
	void testPublished() {
		cpu.execute("LOD", "IMM", 3);
		assertEquals(3,cpu.getAccumulator(),"LOD Imm failed");
		cpu.execute("ADD", "IMM", 5);
		assertEquals(8,cpu.getAccumulator(),"ADD failed");
		cpu.execute("STO", "DIR", 13);
		assertEquals(8,mem.get(13),"STO failed");
		memCpy[13]=8;
		cpu.execute("MUL", "IMM", 3);
		assertEquals(24,cpu.getAccumulator(),"MUL failed");
		cpu.execute("LOD", "DIR", 13);
		assertEquals(8,cpu.getAccumulator(),"LOD dir failed");
		cpu.execute("ADD","IMM", 3);
		assertEquals(11,cpu.getAccumulator(),"Add imm failed");
		checkMem();
	}

	
	void checkMem() {
		for(int i=0;i<100;i++) {
			assertEquals(memCpy[i],mem.get(i),"Memory cell " + i + " incorrect.");
		}
	}
}
