package de.unitrier.st.uap.w25.tram;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class AbstractMachineTests {
    @Test
    void testExample1() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/example1.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{0, 0});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int y = tram.results().get(1);
        assertEquals(28, y, "Expected y = 6*3 + 5*2 = 28.");
        int x = tram.results().get(0);
        assertEquals(6, x, "See test output.");
    }

    @Test
    void testExample2() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/example2.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{10});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        var results = tram.results();
        assertEquals(200, results.get(1), "Expected value 200 since x!=0.");
        assertEquals(3, results.get(2), "Expected final constant 3 on stack.");
        assertEquals(10, results.get(0), "See test output.");
    }

    @Test
    void testExample3() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/example3.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{}); // no initial stack
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(15, result, "Expected return value 15 for f(2,3,4) because y==3.");
    }

    @Test
    void testSquare() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/square.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{}); // empty stack
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(100, result, "Expected result of square(10) = 100.");
    }

    @Test
    void testFibonacci() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/test.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{}); // empty stack
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(8, result, "Expected fib(6) = 8.");
    }

    @Test
    void testWrappedSquare() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/wrapper.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{}); // empty stack
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(4, result, "Expected result of wrapper(4,10) = 4.");
    }

    @Test
    void testMODx5y2() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{17, 5});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(2, result, "Expected 17 mod 5 = 2.");
    }

    @Test
    void testMODx20y5() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{20, 5});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(0, result, "Expected 20 mod 5 = 0.");
    }

    @Test
    void testMODx3y10() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{3, 10});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(3, result, "Expected 3 mod 10 = 3.");
    }

    @Test
    void testMODx37y16() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{37,16});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(5, result, "Expected 37 mod 16 = 5.");
    }

    @Test
    void testMODx100y7() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{100, 7});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(2, result, "Expected 100 mod 7 = 2.");
    }

    @Test
    void testMODx1y10() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{1, 10});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(1, result, "Expected 1 mod 10 = 1.");
    }

    @Test
    void testGGTx17y5() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{17, 5});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(1, result, "Expected ggt(17,5) = 1.");
    }

    @Test
    void testGGTx25y0() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{25, 0});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(25, result, "Expected ggt(25,0) = 25.");
    }

    @Test
    void testGGTx0y25() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{0, 25});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(25, result, "Expected ggt(0,25) = 25.");
    }

    @Test
    void testGGTx270y192() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{270, 192});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(6, result, "Expected ggt(270,192) = 6.");
    }

    @Test
    void testGGTx48y18() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{48, 18});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(6, result, "Expected ggt(48,18) = 6.");
    }

    @Test
    void testGGTx3y9() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{3,9});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(3, result, "Expected ggt(48,18) = 6.");
    }

    @Test
    void testGGTx9y3() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{9,3});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(3, result, "Expected ggt(48,18) = 6.");
    }

    @Test
    void testGGTx24y4() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{24,4});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(4, result, "Expected ggt(48,18) = 6.");
    }

    @Test
    void testGGTx16y37() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{16,37});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(1, result, "Expected ggt(16,37) = 1.");
    }

    @Test
    void testGGTx37y16() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{37,16});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(1, result, "Expected ggt(37,16) = 1.");
    }

    @Test
    void testGGTx100y50() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{100, 50});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(50, result, "Expected ggt(100,50) = 50.");
    }

    @Test
    void testGGTx13y13() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{13, 13});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(13, result, "Expected ggt(13,13) = 13.");
    }

    @Test
    void testGGTx84y36() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{84, 36});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(12, result, "Expected ggt(84,36) = 12.");
    }

    @Test
    void testGGTx1y100() {
        Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
        AbstractMachine tram = new AbstractMachine(code, new Integer[]{1, 100});
        tram.execute();

        assertTrue(tram.isHalted(), "Machine should halt.");
        int result = tram.result();
        assertEquals(1, result, "Expected ggt(1,100) = 1.");
    }
}
