// The MIT License (MIT)
//
// Copyright (C) 2026 Scott Duensing
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

// bmpDraw.c -- Shared 24-bit BMP canvas + drawing primitives for host tools

#include "bmpDraw.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BMP_BYTES_PER_PIXEL 3
#define BMP_HEADER_SIZE     54
#define BMP_DIB_SIZE        40
#define BMP_PLANES          1
#define BMP_BITS_PER_PIXEL  24
#define BMP_ROW_ALIGN       4


void bmpClear(BmpCanvasT *c, uint8_t r, uint8_t g, uint8_t b) {
    bmpFillRect(c, 0, 0, c->w, c->h, r, g, b);
}


void bmpFillCircle(BmpCanvasT *c, int32_t cx, int32_t cy, int32_t radius, uint8_t r, uint8_t g, uint8_t b) {
    int32_t r2 = radius * radius;

    for (int32_t y = -radius; y <= radius; y++) {
        for (int32_t x = -radius; x <= radius; x++) {
            if (x * x + y * y <= r2) {
                bmpPixel(c, cx + x, cy + y, r, g, b);
            }
        }
    }
}


void bmpFillRect(BmpCanvasT *c, int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t r, uint8_t g, uint8_t b) {
    int32_t yEnd = y0 + h;
    int32_t xEnd = x0 + w;

    if (yEnd > c->h) {
        yEnd = c->h;
    }

    if (xEnd > c->w) {
        xEnd = c->w;
    }

    for (int32_t y = y0; y < yEnd; y++) {
        for (int32_t x = x0; x < xEnd; x++) {
            bmpPixel(c, x, y, r, g, b);
        }
    }
}


void bmpFree(BmpCanvasT *c) {
    free(c->pixels);
    c->pixels = NULL;
    c->w      = 0;
    c->h      = 0;
}


void bmpHLine(BmpCanvasT *c, int32_t x0, int32_t y, int32_t w, uint8_t r, uint8_t g, uint8_t b) {
    for (int32_t x = x0; x < x0 + w; x++) {
        bmpPixel(c, x, y, r, g, b);
    }
}


bool bmpInit(BmpCanvasT *c, int32_t w, int32_t h) {
    size_t bytes = (size_t)w * (size_t)h * BMP_BYTES_PER_PIXEL;

    c->pixels = (uint8_t *)calloc(1, bytes);
    c->w      = w;
    c->h      = h;

    return c->pixels != NULL;
}


void bmpPixel(BmpCanvasT *c, int32_t x, int32_t y, uint8_t r, uint8_t g, uint8_t b) {
    if (x >= 0 && x < c->w && y >= 0 && y < c->h) {
        uint8_t *p = c->pixels + ((size_t)y * c->w + x) * BMP_BYTES_PER_PIXEL;

        p[0] = b;
        p[1] = g;
        p[2] = r;
    }
}


void bmpRect(BmpCanvasT *c, int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t r, uint8_t g, uint8_t b) {
    bmpHLine(c, x0,         y0,         w, r, g, b);
    bmpHLine(c, x0,         y0 + h - 1, w, r, g, b);
    bmpVLine(c, x0,         y0,         h, r, g, b);
    bmpVLine(c, x0 + w - 1, y0,         h, r, g, b);
}


void bmpVLine(BmpCanvasT *c, int32_t x, int32_t y0, int32_t h, uint8_t r, uint8_t g, uint8_t b) {
    for (int32_t y = y0; y < y0 + h; y++) {
        bmpPixel(c, x, y, r, g, b);
    }
}


bool bmpWrite(const BmpCanvasT *c, const char *path) {
    int32_t rowBytes = c->w * BMP_BYTES_PER_PIXEL;
    int32_t rowPad   = (BMP_ROW_ALIGN - rowBytes % BMP_ROW_ALIGN) % BMP_ROW_ALIGN;
    int32_t rowSize  = rowBytes + rowPad;
    int32_t dataSize = rowSize * c->h;
    int32_t fileSize = BMP_HEADER_SIZE + dataSize;

    uint8_t header[BMP_HEADER_SIZE];
    memset(header, 0, sizeof(header));

    header[0] = 'B';
    header[1] = 'M';
    *(int32_t *)&header[2]  = fileSize;
    *(int32_t *)&header[10] = BMP_HEADER_SIZE;
    *(int32_t *)&header[14] = BMP_DIB_SIZE;
    *(int32_t *)&header[18] = c->w;
    *(int32_t *)&header[22] = c->h;
    *(int16_t *)&header[26] = BMP_PLANES;
    *(int16_t *)&header[28] = BMP_BITS_PER_PIXEL;
    *(int32_t *)&header[34] = dataSize;

    FILE *f = fopen(path, "wb");

    if (!f) {
        fprintf(stderr, "Cannot write: %s\n", path);
        return false;
    }

    fwrite(header, 1, BMP_HEADER_SIZE, f);

    uint8_t pad[BMP_ROW_ALIGN - 1] = {0};

    // BMP rows are bottom-to-top
    for (int32_t y = c->h - 1; y >= 0; y--) {
        uint8_t *row = c->pixels + (size_t)y * c->w * BMP_BYTES_PER_PIXEL;

        fwrite(row, 1, rowBytes, f);

        if (rowPad > 0) {
            fwrite(pad, 1, rowPad, f);
        }
    }

    fclose(f);
    return true;
}
