/**
 **************************************************
 * @file        Inkplate5v2_Black_And_White.ino
 * @brief       Black & white drawing demo using Adafruit GFX on Soldered Inkplate 5v2.
 *
 * @details     Demonstrates basic drawing and text rendering on the Inkplate 5v2
 *              in 1-bit (black & white) mode using Adafruit GFX-compatible
 *              functions. The example draws pixels, lines, shapes, polygons,
 *              and text in multiple sizes, and also shows how to render a
 *              monochrome bitmap (e.g. Soldered logo) using the Inkplate image
 *              helpers.
 *
 * Requirements:
 * - Board:      Soldered Inkplate 5v2
 * - Hardware:   Inkplate 5v2, USB cable
 * - Extra:      Optional bitmap header file (e.g. logo.h)
 *
 * Configuration:
 * - Boards Manager -> Inkplate Boards -> Soldered Inkplate5v2
 *
 * Don't have Inkplate Boards in Arduino Boards Manager?
 * See https://docs.soldered.com/inkplate/5v2/quick-start-guide/
 *
 * How to use:
 * 1) Upload the sketch to Inkplate 5v2.
 * 2) The example cycles through multiple drawing demonstrations.
 * 3) Each demo renders to the framebuffer and then updates the e-paper display.
 *
 * Expected output:
 * - A sequence of graphics demos: pixels, lines, rectangles, circles, triangles,
 *   rounded rectangles, ellipses, polygons, bitmap drawing, and text rendering.
 * - Final part continuously rotates and displays text.
 *
 * Notes:
 * - Inkplate library is compatible with Adafruit GFX drawing functions.
 * - 1-bit mode supports only BLACK and WHITE colors.
 * - Avoid refreshing the full display too often; long delays are used for demo clarity.
 * - For faster updates, use partial update (see partial update examples).
 *
 * Docs:         https://docs.soldered.com/inkplate
 * Adafruit GFX: https://learn.adafruit.com/adafruit-gfx-graphics-library
 * Support:      https://forum.soldered.com/
 * Image tool:   https://tools.soldered.com/tools/image-converter/
 *
 * @author      Soldered
 * @date        2022-12-01
 * @license     GNU GPL V3
 **************************************************/

// Next 3 lines are a precaution, you can ignore those, and the example would also work without them

#ifndef ARDUINO_INKPLATE5V2
#error "Wrong board selection for this example, please select Soldered Inkplate5 V2 in the boards menu."
#endif

#include "Inkplate.h"            // Include Inkplate library to the sketch
#include "logo.h"                // Include header file for Soldered logo. You can see it in next tab inside Arduino IDE
Inkplate display(INKPLATE_1BIT); // Create object on Inkplate library and set library to work in monochorme mode
// Other option is gray mode, which is demonstrated in next example "Inkplate5V2_Grayscale"

// Delay in milliseconds between screen refresh. Refreshing e-paper screens more often than 5s is not recommended
#define DELAY_MS 5000
// Want to refresh faster? Use partial update! Find example in "Inkplate5V2_Partial_Update"

void setup()
{
    display.begin();        // Init library (you should call this function ONLY ONCE)
    display.clearDisplay(); // Clear any data that may have been in (software) frame buffer.
    //(NOTE! This does not clean image on screen, it only clears it in the frame buffer inside ESP32).
    display.setCursor(266, 345);
    display.setTextColor(BLACK, WHITE); // Set black text with white background
    display.setTextSize(4);             // Set text size 4 times bigger than original (5 x 7 px)
    display.print("Welcome to Inkplate 5 V2!");
    display.display(); // Write hello message on the screen
    delay(5000);       // Wait a little bit
}

void loop()
{
    // Example will demostrate funcionality one by one. You always first set everything in the frame buffer and
    // afterwards you show it on the screen using display.display().

    // Let's start by drawing a pixel in the middle of the screen
    display.clearDisplay(); // Clear everytning that is inside frame buffer in ESP32
    displayCurrentAction(
        "Drawing a pixel"); // Function which writes small text at bottom left indicating what's currently done
                            // NOTE: you do not need displayCurrentAction function to use Inkplate!
    display.drawPixel(E_INK_WIDTH / 2, E_INK_HEIGHT / 2,
                      BLACK); // Draw one black pixel at X = E_INK_WIDTH/2 (640), Y = E_INK_HEIGHT/2 (360) position in
                              // BLACK color (must be black since Inkplate is in BW mode)
    display.display(); // Send image to display. You need to call this one each time you want to transfer frame buffer
                       // to the screen.
    delay(DELAY_MS);   // Wait a little bit

    // Now, let's draw some random pixels!
    display.clearDisplay(); // Clear everything that is inside frame buffer in ESP32
    for (int i = 0; i < 600; i++)
    {
        // Write 600 black pixels at random locations
        display.drawPixel(random(0, E_INK_WIDTH), random(0, E_INK_HEIGHT), BLACK);
    }
    displayCurrentAction("Drawing 600 random pixels");
    display.display(); // Write everything from frame buffer to screen
    delay(DELAY_MS);   // Wait

    // Draw two diagonal lines accros screen
    display.clearDisplay();
    display.drawLine(
        0, 0, E_INK_WIDTH, E_INK_HEIGHT,
        BLACK); // All of those drawing fuctions originate from Adafruit GFX library, so maybe you are already familiar
    display.drawLine(E_INK_WIDTH, 0, 0, E_INK_HEIGHT,
                     BLACK); // with those. Arguments are: start X, start Y, ending X, ending Y, color.
    displayCurrentAction("Drawing two diagonal lines");
    display.display();
    delay(DELAY_MS);

    // And again, let's draw some random lines on screen!
    display.clearDisplay();
    for (int i = 0; i < 50; i++)
    {
        display.drawLine(random(0, E_INK_WIDTH), random(0, E_INK_HEIGHT), random(0, E_INK_WIDTH),
                         random(0, E_INK_HEIGHT), BLACK);
    }
    displayCurrentAction("Drawing 50 random lines");
    display.display();
    delay(DELAY_MS);

    // Let's draw some random thick lines on the screen!
    display.clearDisplay();
    for (int i = 0; i < 50; i++)
    {
        display.drawThickLine(random(0, E_INK_WIDTH), random(0, E_INK_HEIGHT), random(0, E_INK_WIDTH),
                              random(0, E_INK_HEIGHT), BLACK, (float)random(1, 20));
    }
    displayCurrentAction("Drawing 50 random thick lines");
    display.display();
    delay(DELAY_MS);

    // Now draw a horizontal...
    display.clearDisplay();
    display.drawFastHLine(239, 359, 798, BLACK); // Arguments are: starting X, starting Y, length, color
    displayCurrentAction("Drawing one horizontal line");
    display.display();
    delay(DELAY_MS);

    //... and one vertical line
    display.clearDisplay();
    display.drawFastVLine(638, 93, 532, BLACK); // Arguments are: starting X, starting Y, length, color
    displayCurrentAction("Drawing one vertical line");
    display.display();
    delay(DELAY_MS);

    // Now, let' make a grid using only horizontal and vertical lines
    display.clearDisplay();
    for (int i = 0; i < E_INK_WIDTH; i += 8)
    {
        display.drawFastVLine(i, 0, E_INK_HEIGHT, BLACK);
    }
    for (int i = 0; i < E_INK_HEIGHT; i += 8)
    {
        display.drawFastHLine(0, i, E_INK_WIDTH, BLACK);
    }
    displayCurrentAction("Drawing a grid using horizontal and vertical lines");
    display.display();
    delay(DELAY_MS);

    // Draw a rectangle in the center of the screen with a size of 540 x 400 pixels
    display.clearDisplay();
    display.drawRect(372, 159, 540, 400, BLACK); // Arguments are: start X, start Y, size X, size Y, color
    displayCurrentAction("Drawing rectangle");
    display.display();
    delay(DELAY_MS);

    // Draw rectangles on random location, size 100x150 pixels
    display.clearDisplay();
    for (int i = 0; i < 50; i++)
    {
        display.drawRect(random(0, E_INK_WIDTH), random(0, E_INK_HEIGHT), 100, 150, BLACK);
    }
    displayCurrentAction("Drawing many rectangles");
    display.display();
    delay(DELAY_MS);

    // Draw filled black rectangle at X = 372, Y = 159, size of 540x540 pixels
    display.clearDisplay();
    display.fillRect(372, 159, 540, 400, BLACK); // Arguments are: start X, start Y, size X, size Y, color
    displayCurrentAction("Drawing black rectangle");
    display.display();
    delay(DELAY_MS);

    // Draw filled black rectangles on random location, size of 30x30 pixels
    display.clearDisplay();
    for (int i = 0; i < 50; i++)
    {
        display.fillRect(random(0, E_INK_WIDTH), random(0, E_INK_HEIGHT), 30, 30, BLACK);
    }
    displayCurrentAction("Drawing many filled rectangles randomly");
    display.display();
    delay(DELAY_MS);

    // Draw circle at center of a screen with radius of 75 pixels
    display.clearDisplay();
    display.drawCircle(E_INK_WIDTH / 2, E_INK_HEIGHT / 2, 75, BLACK); // Arguments are: start X, start Y, radius, color
    displayCurrentAction("Drawing a circle");
    display.display();
    delay(DELAY_MS);

    // Draw some circles at random location with radius of 25 pixels
    display.clearDisplay();
    for (int i = 0; i < 40; i++)
    {
        display.drawCircle(random(0, E_INK_WIDTH), random(0, E_INK_HEIGHT), 25, BLACK);
    }
    displayCurrentAction("Drawing many circles randomly");
    display.display();
    delay(DELAY_MS);

    // Draw black filled circle at center of a screen with radius of 75 pixels
    display.clearDisplay();
    display.fillCircle(E_INK_WIDTH / 2, E_INK_HEIGHT / 2, 75, BLACK); // Arguments are: start X, start Y, radius, color
    displayCurrentAction("Drawing black-filled circle");
    display.display();
    delay(DELAY_MS);

    // Draw some black filled circles at random location with radius of 15 pixels
    display.clearDisplay();
    for (int i = 0; i < 40; i++)
    {
        display.fillCircle(random(0, E_INK_WIDTH), random(0, E_INK_HEIGHT), 15, BLACK);
    }
    displayCurrentAction("Drawing many filled circles randomly");
    display.display(); // To show stuff on screen, you always need to call display.display();
    delay(DELAY_MS);

    // Draw rounded rectangle at X = 372, Y = 159 and size of 540x400 pixels and radius of 13 pixels
    display.clearDisplay();
    display.drawRoundRect(372, 159, 540, 400, 13,
                          BLACK); // Arguments are: start X, start Y, size X, size Y, radius, color
    displayCurrentAction("Drawing rectangle with rounded edges");
    display.display();
    delay(DELAY_MS);

    // Draw rounded rectangles on random location, size 100x150 pixels, radius of 5 pixels
    display.clearDisplay();
    for (int i = 0; i < 50; i++)
    {
        display.drawRoundRect(random(0, E_INK_WIDTH), random(0, E_INK_HEIGHT), 100, 150, 5, BLACK);
    }
    displayCurrentAction("Drawing many rounded edges rectangles");
    display.display();
    delay(DELAY_MS);

    // Draw filled black rect at X = 372, Y = 159, size of 540x400 pixels and radius of 13 pixels
    display.clearDisplay();
    display.fillRoundRect(372, 159, 540, 400, 13,
                          BLACK); // Arguments are: start X, start Y, size X, size Y, radius, color
    displayCurrentAction("This is filled rectangle with rounded edges");
    display.display();
    delay(DELAY_MS);

    // Draw filled black rects on random location, size of 30x30 pixels, radius of 3 pixels
    display.clearDisplay();
    for (int i = 0; i < 50; i++)
    {
        display.fillRoundRect(random(0, E_INK_WIDTH), random(0, E_INK_HEIGHT), 30, 30, 3, BLACK);
    }
    displayCurrentAction("Random rounded edge filled rectangles");
    display.display();
    delay(DELAY_MS);

    // Draw simple triangle
    display.clearDisplay();
    display.drawTriangle(405, 585, 638, 119, 871, 585, BLACK); // Arguments are: X1, Y1, X2, Y2, X3, Y3, color
    displayCurrentAction("Drawing triangle");
    display.display();
    delay(DELAY_MS);

    // Draw filled triangle inside simple triangle (so no display.clearDisplay() this time)
    display.fillTriangle(505, 518, 638, 252, 771, 518, BLACK); // Arguments are: X1, Y1, X2, Y2, X3, Y3, color
    displayCurrentAction("Drawing triangle inside exsisting one");
    display.display();
    delay(DELAY_MS);

    // Display some bitmap on screen. We are going to display Soldered logo on display at location X = 100, Y = 250
    // Image is 800x160 pixels and we want to every pixel of this bitmap to be black.
    display.clearDisplay();
    display.image.draw(logo, 100, 250, logo_w, logo_h,
                      BLACK); // Arguments are: array variable name, start X, start Y, size X, size Y, color
    displayCurrentAction("Drawing Soldered logo");
    display.display();
    delay(DELAY_MS);


    // Write some text on screen with different sizes
    display.clearDisplay();
    for (int i = 0; i < 6; i++)
    {
        display.setTextSize(i + 1);                // textSize parameter starts at 1 and goes up to 6
        display.setCursor(50, 230 + (i * i * 16)); // setCursor works as same as on LCD displays - sets "the cursor" at
                                                   // the place you want to write someting next
        display.print("INKPLATE 5 V2!");              // The actual text you want to show on e-paper as String
    }
    displayCurrentAction("Text in different sizes and shadings");
    display.display(); // To show stuff on screen, you always need to call display.display();
    delay(DELAY_MS);

    // Write same text on different location, but now invert colors (text is white, text background is black), without
    // cleaning the previous text
    display.setTextColor(WHITE, BLACK); // First argument is text color, while second argument is background color. In
                                        // BW, there are only two options: BLACK & WHITE
    for (int i = 0; i < 6; i++)
    {
        display.setTextSize(i + 1);
        display.setCursor(650, 230 + (i * i * 16));
        display.print("INKPLATE 5 V2!");
    }
    display.display();
    delay(DELAY_MS);

    // Draws an elipse with x radius, y radius, center x, center y and color
    display.clearDisplay();
    display.drawElipse(133, 266, E_INK_WIDTH / 2, E_INK_HEIGHT / 2, BLACK);
    displayCurrentAction("Drawing an elipse");
    display.display();

    delay(DELAY_MS);

    // Fills an elipse with x radius, y radius, center x, center y and color
    display.clearDisplay();
    display.fillElipse(133, 266, E_INK_WIDTH / 2, E_INK_HEIGHT / 2, BLACK);
    displayCurrentAction("Drawing a filled elipse");
    display.display();

    delay(DELAY_MS);

    // Code block for generating random points and sorting them in a counter
    // clockwise direction.
    int xt[10];
    int yt[10];
    int n = 10;
    for (int i = 0; i < n; ++i)
    {
        xt[i] = random(100, E_INK_WIDTH);
        yt[i] = random(100, E_INK_HEIGHT);
    }
    int k;
    for (int i = 0; i < n - 1; ++i)
        for (int j = i + 1; j < n; ++j)
            if (atan2(yt[j] - 300, xt[j] - 400) < atan2(yt[i] - 300, xt[i] - 400))
            {
                k = xt[i], xt[i] = xt[j], xt[j] = k;
                k = yt[i], yt[i] = yt[j], yt[j] = k;
            }

    // Draws a polygon, from x and y coordinate arrays of n points in color c
    display.clearDisplay();
    display.drawPolygon(xt, yt, n, BLACK);
    displayCurrentAction("Drawing a polygon");
    display.display();

    delay(DELAY_MS);

    // Fills a polygon, from x and y coordinate arrays of n points in color c,
    // Points need to be counter clockwise sorted
    // Method can be quite slow for now, probably will improve
    display.clearDisplay();
    display.fillPolygon(xt, yt, n, BLACK);
    displayCurrentAction("Drawing a filled polygon");
    display.display();

    delay(DELAY_MS);

    // Write text and rotate it by 90 deg. forever
    int r = 0;
    display.setTextSize(8);
    display.setTextColor(WHITE, BLACK);
    while (true)
    {
        display.setCursor(100, 100);
        display.clearDisplay();
        display.setRotation(
            r); // Set rotation will sent rotation for the entire display, so you can use it sideways or upside-down
        display.print("INKPLATE5 V2");
        display.display();
        r++;
        delay(DELAY_MS);
    }

    // Did you know that you can change between BW and greyscale mode anytime?
    // Just call display.selectDisplayMode(mode)
}

// Small function that will write on the screen what function is currently in demonstration.
void displayCurrentAction(String text)
{
    display.setTextSize(2);
    display.setCursor(10, 700);
    display.print(text);
}
