/**
 **************************************************
 * @file        Inkplate4TEMPERA_Maze_Generator.ino
 * @brief       Generate and draw a random maze in 1-bit (BW) mode on Inkplate
 *              4 TEMPERA.
 *
 * @details     This example creates a new random maze on each reset and renders
 *              it to the Inkplate 4 TEMPERA e-paper display. A simple maze
 *              generation algorithm carves passages in a grid of cells and the
 *              resulting connectivity is drawn as line segments between adjacent
 *              open cells.
 *
 *              The maze is drawn fully into the frame buffer and then shown on
 *              the panel using a full refresh (display.display()). The generated
 *              maze is intended to be a printable/puzzle-style layout—you can
 *              solve it directly on the screen with an erasable marker or soft
 *              pencil, provided you avoid permanent inks.
 *
 * Requirements:
 * - Board:      Soldered Inkplate 4 TEMPERA
 * - Hardware:   Inkplate 4 TEMPERA, USB-C cable
 * - Extra:      none
 *
 * Configuration:
 * - Boards Manager -> Inkplate Boards -> Soldered Inkplate 4 TEMPERA
 * - Serial settings (if relevant): 115200 baud (optional; not required)
 *
 * Don't have Inkplate Boards in Arduino Boards Manager?
 * See https://docs.soldered.com/inkplate/10/quick-start-guide/
 *
 * How to use:
 * 1) Select the Inkplate 4 TEMPERA board and upload the sketch.
 * 2) After boot, a maze is generated and displayed.
 * 3) Press reset (or power-cycle) to generate a new random maze.
 * 4) Optionally solve it on the screen using erasable tools.
 *
 * Expected output:
 * - E-paper: A maze drawn in black lines with an entry and exit opening.
 *
 * Notes:
 * - Display mode is 1-bit (BW).
 * - This sketch performs a single full refresh and then stays idle.
 * - Cell size controls maze density; smaller cellSize increases detail but may
 *   increase generation and drawing time.
 * - If writing on the panel, use only non-permanent tools (whiteboard marker,
 *   soft graphite). Avoid permanent markers to prevent staining.
 *
 * Docs:         https://docs.soldered.com/inkplate
 * Support:      https://forum.soldered.com/
 *
 * @author      Soldered
 * @date        2023-07-24
 * @license     GNU GPL V3
 **************************************************/

// Next 3 lines are a precaution, you can ignore those, and the example would also work without them
#ifndef ARDUINO_INKPLATE4TEMPERA
#error "Wrong board selection for this example, please select Inkplate 4 TEMPERA in the boards menu."
#endif

#include "Inkplate.h"

// Initialise Inkplate object
Inkplate display(INKPLATE_1BIT);

// Here we define one cell size
const int cellSize = 20;

// Calculate screen width and height
const int w = (E_INK_WIDTH - 60) / cellSize, h = (E_INK_HEIGHT - 60) / cellSize;
char maze[w * h];

// Move direction difference array
int dx[] = {-1, 0, 0, 1};
int dy[] = {0, -1, 1, 0};

void setup()
{
    // Initialise Inkplate
    Serial.begin(115200);
    display.begin();
    
    // Generate and display the maze
    generateMaze(maze, w, h);
    showMaze(maze, w, h);
}

void loop()
{
}

// Display the maze
void showMaze(const char *maze, int width, int height)
{
    for (int y = 0; y < height; ++y)
        for (int x = 0; x < width; ++x)
            if (maze[x + y * width] == 1)
                for (int i = 0; i < 4; ++i)
                {
                    int xx = x + dx[i];
                    int yy = y + dy[i];
                    if (0 <= xx && xx < w && 0 <= yy && yy < h && maze[yy * width + xx] == 1)
                        display.drawLine(3 + x * cellSize + cellSize / 2 + 30, 3 + y * cellSize + cellSize / 2 + 30,
                                         3 + x * cellSize + cellSize / 2 + (dx[i] * cellSize / 2) + 30,
                                         3 + y * cellSize + cellSize / 2 + (dy[i] * cellSize / 2) + 30, BLACK);
                }

    display.display();
}

// Carve the maze starting at x, y.
void carveMaze(char *maze, int width, int height, int x, int y)
{
    int x1, y1;
    int x2, y2;
    int dx, dy;
    int dir, count;

    dir = random(4);
    count = 0;
    while (count < 4)
    {
        dx = 0;
        dy = 0;
        switch (dir)
        {
        case 0:
            dx = 1;
            break;
        case 1:
            dy = 1;
            break;
        case 2:
            dx = -1;
            break;
        default:
            dy = -1;
            break;
        }
        x1 = x + dx;
        y1 = y + dy;
        x2 = x1 + dx;
        y2 = y1 + dy;
        if (x2 > 0 && x2 < width && y2 > 0 && y2 < height && maze[y1 * width + x1] == 1 && maze[y2 * width + x2] == 1)
        {
            maze[y1 * width + x1] = 0;
            maze[y2 * width + x2] = 0;
            x = x2;
            y = y2;
            dir = random(4);
            count = 0;
        }
        else
        {
            dir = (dir + 1) % 4;
            count += 1;
        }
    }
}

// Generate maze in matrix maze with size width, height.
void generateMaze(char *maze, int width, int height)
{
    int x, y;

    // Initialize the maze.
    for (x = 0; x < width * height; x++)
    {
        maze[x] = 1;
    }
    maze[1 * width + 1] = 0;

    // Seed the random number generator.
    srand(time(0));

    // Carve the maze.
    for (y = 1; y < height; y += 2)
    {
        for (x = 1; x < width; x += 2)
        {
            carveMaze(maze, width, height, x, y);
        }
    }

    // Set up the entry and exit.
    maze[0 * width + 1] = 0;
    maze[(height - 1) * width + (width - 2)] = 0;
}
