#include "monoformat_schema.hpp"
#include "monoformat_bithelpers.hpp"

#include <tuple>

namespace monoformat {

MemoryOneBitBuffer::MemoryOneBitBuffer(std::span<std::byte> buffer, std::uint16_t width, std::uint16_t height)
    : m_width{width}
    , m_height{height}
    , m_buffer{buffer}
{
    std::size_t expectedSize = (m_width * m_height + 8 - 1) / 8;
    if (m_buffer.size() < expectedSize) {
        m_width = 0;
        m_height = 0;
    }
}

MemoryOneBitBuffer::~MemoryOneBitBuffer() {
}

bool MemoryOneBitBuffer::isPixelSet(std::uint16_t x, std::uint16_t y) const {
    if (x >= m_width || y >= m_height) {
        return false;
    }
    std::size_t pxIndex = static_cast<std::size_t>(y) * m_width + x;
    std::size_t byteIndex = pxIndex / 8;
    std::size_t bitIndex = pxIndex % 8;
    return isBitSet(m_buffer[byteIndex], bitIndex);
}

void MemoryOneBitBuffer::setPixel(std::uint16_t x, std::uint16_t y, bool value) {
    if (x >= m_width || y >= m_height) {
        return;
    }
    std::size_t pxIndex = static_cast<std::size_t>(y) * m_width + x;
    std::size_t byteIndex = pxIndex / 8;
    std::size_t bitIndex = pxIndex % 8;
    maybeSetBit(m_buffer[byteIndex], bitIndex, value);
}

ConstMemoryOneBitBuffer::ConstMemoryOneBitBuffer(std::span<std::byte const> buffer, std::uint16_t width, std::uint16_t height)
    : m_width{width}
    , m_height{height}
    , m_buffer{buffer}
{
    std::size_t expectedSize = (m_width * m_height + 8 - 1) / 8;
    if (m_buffer.size() < expectedSize) {
        m_width = 0;
        m_height = 0;
    }
}

ConstMemoryOneBitBuffer::~ConstMemoryOneBitBuffer() {
}

bool ConstMemoryOneBitBuffer::isPixelSet(std::uint16_t x, std::uint16_t y) const {
    if (x >= m_width || y >= m_height) {
        return false;
    }
    std::size_t pxIndex = static_cast<std::size_t>(y) * m_width + x;
    std::size_t byteIndex = pxIndex / 8;
    std::size_t bitIndex = pxIndex % 8;
    return isBitSet(m_buffer[byteIndex], bitIndex);
}

void ConstMemoryOneBitBuffer::setPixel(std::uint16_t x, std::uint16_t y, bool value) {
    std::ignore = x;
    std::ignore = y;
    std::ignore = value;
}

ClippedImage::ClippedImage(OneBitBufferInterface* underlying, std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height)
    : m_underlying{underlying}
    , m_x{x}
    , m_y{y}
    , m_width{width}
    , m_height{height}
{
}

ClippedImage::~ClippedImage() {
}

bool ClippedImage::isPixelSet(std::uint16_t x, std::uint16_t y) const {
    if (x >= m_width || y >= m_height) {
        return false;
    }
    return m_underlying->isPixelSet(x + m_x, y + m_y);
}

void ClippedImage::setPixel(std::uint16_t x, std::uint16_t y, bool value) {
    if (x >= m_width || y >= m_height) {
        return;
    }
    m_underlying->setPixel(x + m_x, y + m_y, value);
}

} // namespace monoformat
