package dotsandboxes;

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

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class DotsAndBoxesGridTest {
    /*
     * Because Test classes are classes, they can have fields, and can have static fields.
     * This field is a logger. Loggers are like a more advanced println, for writing messages out to the console or a log file.
     */
    private static final Logger logger = LogManager.getLogger(DotsAndBoxesGridTest.class);

    /*
     * Tests are functions that have an @Test annotation before them.
     * The typical format of a test is that it contains some code that does something, and then one
     * or more assertions to check that a condition holds.
     *
     * This is a dummy test just to show that the test suite itself runs
     */
    @Test
    public void testTestSuiteRuns() {
        logger.info("Dummy test to show the test suite runs");
        assertTrue(true);
    }

    /**
     * Tests correct detection of completed boxes
     */
    @Test
    public void boxCompleteDetectsCompletedBoxes() {
        // test top left corner box of grid
        DotsAndBoxesGrid case1 = new DotsAndBoxesGrid(5, 5, 2);
        case1.drawHorizontal(0, 0, 0);
        case1.drawVertical(0, 0, 1);
        case1.drawHorizontal(0, 1, 0);
        case1.drawVertical(1, 0, 1);

        assertTrue(case1.boxComplete(0,0));

        // test bottom right corner box of grid
        DotsAndBoxesGrid case2 = new DotsAndBoxesGrid(5, 5, 2);
        case2.drawHorizontal(3, 3, 0);
        case2.drawVertical(3, 3, 1);
        case2.drawHorizontal(3, 4, 0);
        case2.drawVertical(4, 3, 1);

        assertTrue(case2.boxComplete(3,3));

        // test grid with 1 box
        DotsAndBoxesGrid case3 = new DotsAndBoxesGrid(2, 2, 2);
        case3.drawHorizontal(0, 0, 0);
        case3.drawVertical(0, 0, 1);
        case3.drawHorizontal(0, 1, 0);
        case3.drawVertical(1, 0, 1);

        assertTrue(case3.boxComplete(0,0));

        // test a box around the center of grid
        DotsAndBoxesGrid case4 = new DotsAndBoxesGrid(5, 5, 2);
        case4.drawHorizontal(2, 2, 0);
        case4.drawVertical(2, 2, 1);
        case4.drawHorizontal(2, 3, 0);
        case4.drawVertical(3, 2, 1);

        assertTrue(case4.boxComplete(2,2));
    }

    /**
     * Tests correct detection of incomplete boxes
     */
    @Test
    public void boxCompleteDetectsIncompleteBoxes() {
        // test top left corner box of grid missing top horizontal line
        DotsAndBoxesGrid case1 = new DotsAndBoxesGrid(5, 5, 2);
        case1.drawVertical(0, 0, 1);
        case1.drawHorizontal(0, 1, 0);
        case1.drawVertical(1, 0, 1);

        assertFalse(case1.boxComplete(0,0));

        // test top left corner box of grid missing bottom horizontal line
        DotsAndBoxesGrid case2 = new DotsAndBoxesGrid(5, 5, 2);
        case2.drawHorizontal(0, 0, 0);
        case2.drawVertical(0, 0, 1);
        case2.drawVertical(1, 0, 1);

        assertFalse(case2.boxComplete(0,0));

        // test top left corner box of grid missing left vertical line
        DotsAndBoxesGrid case3 = new DotsAndBoxesGrid(5, 5, 2);
        case3.drawHorizontal(0, 0, 0);
        case3.drawHorizontal(0, 1, 0);
        case3.drawVertical(1, 0, 1);

        assertFalse(case3.boxComplete(0,0));

        // test top left corner box of grid missing right vertical line
        DotsAndBoxesGrid case4 = new DotsAndBoxesGrid(5, 5, 2);
        case4.drawHorizontal(0, 0, 0);
        case4.drawVertical(0, 0, 1);
        case4.drawHorizontal(0, 1, 0);

        assertFalse(case4.boxComplete(0,0));

        // test bottom right corner box of grid missing top horizontal line
        DotsAndBoxesGrid case5 = new DotsAndBoxesGrid(5, 5, 2);
        case5.drawVertical(3, 3, 1);
        case5.drawHorizontal(3, 4, 0);
        case5.drawVertical(4, 3, 1);

        assertFalse(case5.boxComplete(3,3));

        // test grid with 1 box missing top horizontal line
        DotsAndBoxesGrid case6 = new DotsAndBoxesGrid(2, 2, 2);
        case6.drawVertical(0, 0, 1);
        case6.drawHorizontal(0, 1, 0);
        case6.drawVertical(1, 0, 1);

        assertFalse(case6.boxComplete(0,0));

        // test a box around the center of grid missing top horizontal line
        DotsAndBoxesGrid case7 = new DotsAndBoxesGrid(5, 5, 2);
        case7.drawVertical(2, 2, 1);
        case7.drawHorizontal(2, 3, 0);
        case7.drawVertical(3, 2, 1);

        assertFalse(case7.boxComplete(2,2));
    }

    /**
     * Tests that IllegalStateException is thrown when drawHorizontal() or drawVertical() is called
     * on a line that was already drawn.
     */
    @Test
    public void drawMethodsDetectRedrawnLines() {
        DotsAndBoxesGrid case1 = new DotsAndBoxesGrid(5, 5, 2);

        // tests drawing on previously-drawn horizontal lines

        case1.drawHorizontal(0, 0, 0);
        assertThrows(IllegalStateException.class, () -> {
            case1.drawHorizontal(0,0,1);
        });

        case1.drawHorizontal(2, 2, 0);
        assertThrows(IllegalStateException.class, () -> {
            case1.drawHorizontal(2,2,1);
        });

        case1.drawHorizontal(3, 4, 0);
        assertThrows(IllegalStateException.class, () -> {
            case1.drawHorizontal(3,4,1);
        });

        // tests drawing on previously-drawn vertical lines

        case1.drawVertical(0, 0, 0);
        assertThrows(IllegalStateException.class, () -> {
            case1.drawVertical(0,0,1);
        });

        case1.drawVertical(2, 2, 0);
        assertThrows(IllegalStateException.class, () -> {
            case1.drawVertical(2,2,1);
        });

        case1.drawVertical(4, 3, 0);
        assertThrows(IllegalStateException.class, () -> {
            case1.drawVertical(4,3,1);
        });
    }
}