/*#############################################################################
## Author: Shaun Reed                                                        ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved               ##
##                                                                           ##
## Contact: shaunrd0@gmail.com  | URL: www.shaunreed.com                     ##
##############################################################################
*/

/**
 * Calculate byte offset for the pixel at [x,y] within a horizontally-mapped
 * monochrome uint8 draw buffer, using the initialized horizontal resolution.
 *
 * We use `>> 3` because each pixel requires 1 bit, but each uint8 in the draw
 * buffer can hold 8 bits. To find the uint8 value in our draw buffer that
 * stores this pixel's value we must compensate for this when using pixel
 * coordinates in byte math.
 *
 * Therefore, each uint8 in the draw buffer stores the state of 8 pixels.
 * Below is an example of calculating for [x, y] pixel coordinates [20, 10].
 * The example uses a horizontal resolution of 128.
 *
 * For the horizontal case, each row (y) of the image is represented by
 * `hor_res >> 3` bytes (16). The byte-offset of the first pixel in the 10th
 * row for example is `16 * 10` = 160.
 *
 * Since the pixels are stored horizontally we must calculate the 20th pixel
 * column (x) as `160 + (20 >> 3)`, or `160 + (20 / 8)` to get a final offset
 * of 162.
 *
 * @param x X pixel coordinate to find byte offset.
 * @param y Y pixel coordinate to find byte offset.
 * @param hor_res horizontal resolution of the display.
 * @return byte offset for a single-byte monochrome pixel at [x,y].
 */
static ptrdiff_t horizontal_byte_offset_long(const int32_t x, const int32_t y,
                                             const int32_t hor_res)
{
    // Convert pixel (bit) coordinates to byte coordinates in the draw buffer.
    return (hor_res >> 3) * y + (x >> 3);
}

/**
 * Calculate byte offset for the pixel at [x,y] within a vertically-mapped
 * monochrome uint8 draw buffer, using the initialized horizontal resolution.
 *
 * We use `>> 3` because each pixel requires 1 bit, but each uint8 in the draw
 * buffer can hold 8 bits. To find the uint8 value in our draw buffer that
 * stores this pixel's value we must compensate for this when using pixel
 * coordinates in byte math.
 *
 * Therefore, each uint8 in the draw buffer stores the state of 8 pixels.
 * Below is an example of calculating for [x, y] pixel coordinates [20, 10].
 * The example uses a horizontal resolution of 128.
 *
 * For the vertical case, each row (y) of the image is represented by
 * `hor_res` bytes (128) - one for each column (x). Because the pixels are
 * stored vertically, the byte-offset of the first pixel in the 10th row is
 * `128 * (10 >> 3)` or * `128 * (10 / 8)` = 128.
 *
 * From this location we can simply calculate the 20th pixel column (x) as
 * `128 + 20` to get a final offset of 148, because the pixels are stored in a
 * columnar format.
 *
 * @param x X pixel coordinate to find byte offset.
 * @param y Y pixel coordinate to find byte offset.
 * @param hor_res horizontal resolution of the display.
 * @return byte offset for a single-byte monochrome pixel at [x,y].
 */
static ptrdiff_t vertical_byte_offset_long(const int32_t x, const int32_t y,
                                           const int32_t hor_res)
{
    // Convert pixel (bit) coordinates to byte coordinates in the draw buffer.
    return hor_res * (y >> 3) + x;
}

/**
 * Finds the Most Significant Bit location of bit `i` in a byte.
 *
 *          MSB           LSB
 * bits      7 6 5 4 3 2 1 0
 * data      8 7 6 5 4 3 2 1
 *          Left         Right
 *
 * @return bitmask for MSB location of `i`.
 */
static uint8_t msb_mask(const int32_t i) { return 1 << (7 - i % 8); }

/**
 * Finds the Least Significant Bit location of bit `i` in a byte.
 *
 *          LSB           MSB
 * bits      0 1 2 3 4 5 6 7
 * data      1 2 3 4 5 6 7 8
 *          Left         Right
 *
 * @return bitmask for LSB location of `i`.
 */
static uint8_t lsb_mask(const int32_t i) { return 1 << (i % 8); }
