/*
 * Copyright (C) 2024 dwlr <dweller@cabin.digital>
 *
 * BSD 3-Clause License (BSD-3-Clause)
 * See LICENSE for details
 */

typedef struct packed tga_head
{
    u8 id_len;
    u8 cmap_type;
    u8 img_type;

    struct packed
    {
        u16 first_entry_idx;
        u16 length;
        u8  entry_size;

    } cmap_spec;

    struct packed
    {
        u16 xorig;
        u16 yorig;
        u16 width;
        u16 height;
        u8  depth;
        u8  desc;

    } img_spec;

} tga_head;

typedef enum tga_img_type
{
    TGA_IMG_NONE,
    TGA_IMG_RAW_CMAP,
    TGA_IMG_RAW_TRUE,
    TGA_IMG_RAW_BW,
    TGA_IMG_RLE_CMAP,
    TGA_IMG_RLE_TRUE,
    TGA_IMG_RLE_BW,

    TGA_IMG_TYPE_SIZE

} tga_img_type;

typedef union rgba
{
    u32 rgba;
    struct
    {
        u8 b;
        u8 g;
        u8 r;
        u8 a;
    } c;

} rgba;

#define A c.a
#define R c.r
#define G c.g
#define B c.b


typedef struct texture
{
    ssize width;
    ssize height;

    rgba* texels;

} texture;


/* FIXME: return 1x1 magenta tex on fail? */
bool tga2tex_from_mem(texture* tex, const u8* data)
{
    tga_head* hdr = NULL;
    rgba*     raw = NULL;
    usize     sz  = 0;

    assert(tex);
    assert(data);

    hdr = (tga_head*)data;
    if(hdr->img_type != TGA_IMG_RAW_TRUE) return false;
    if(hdr->cmap_type) return false;

    /* FIXME: check pixel depth */
    /* FIXME: check bit 5 and 4 of img_spec.desc (ordering) */

    tex->width  = hdr->img_spec.width;
    tex->height = hdr->img_spec.height;

    sz = sizeof(tex->texels[0]) * tex->width * tex->height;
    tex->texels = malloc(sz);
    if(!tex->texels) return false;

    raw = (rgba*)(((u8*)hdr) + sizeof(*hdr) + hdr->id_len);

    /* FIXME: convert if needed */
    /* XXX: possibly misaligned */
    memcpy(tex->texels, raw, sz);

    return true;
}


bool tga2tex_from_file(texture* tex, const char* path)
{
    struct stat stat = {0};

    bool   ret  = false;
    s32    fd   = -1;
    u8*    file = NULL;

    assert(tex);
    assert(path);

    fd = open(path, O_RDONLY);
    if(fd < 0) goto clean_close;

    if(fstat(fd, &stat) != 0) goto clean_close;

    file = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if(file == MAP_FAILED) goto clean_close;

    ret = tga2tex_from_mem(tex, file);

    munmap(file, stat.st_size);
clean_close:
    close(fd);

    return ret;
}