/* BMP Header
 * ==========
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define PIXELS_PER_METER (2835)     /* 72 dpi */

/* BMP file structures */

typedef struct {
    uint32_t     filesz;
    uint16_t     creator1;
    uint16_t     creator2;
    uint32_t     bmp_offset;
} bmpfile_header;

typedef struct {
    uint32_t     header_sz;
    int32_t      width;
    int32_t      height;
    uint16_t     nplanes;
    uint16_t     bitspp;
    uint32_t     compress_type;
    uint32_t     bmp_bytesz;
    int32_t      hres;
    int32_t      vres;
    uint32_t     ncolors;
    uint32_t     nimpcolors;
} BITMAPINFOHEADER;

typedef struct {
    uint32_t     bV4Size;
    int32_t      bV4Width;
    int32_t      bV4Height;
    uint16_t     bV4Planes;
    uint16_t     bV4BitCount;
    uint32_t     bV4V4Compression;
    uint32_t     bV4SizeImage;
    int32_t      bV4XPelsPerMeter;
    int32_t      bV4YPelsPerMeter;
    uint32_t     bV4ClrUsed;
    uint32_t     bV4ClrImportant;
    uint32_t     bV4AlphaMask;
    uint32_t     bV4RedMask;
    uint32_t     bV4GreenMask;
    uint32_t     bV4BlueMask;    
    uint32_t     bV4CSType;
    char         bV4Endpoints[24];
    uint32_t     bV4GammaRed;
    uint32_t     bV4GammaGreen;
    uint32_t     bV4GammaBlue;
} BITMAPV4HEADER;

/* Helper functions */

bmpfile_header make_bfh(uint32_t datasize, size_t sizeof_bmpheader) {
    bmpfile_header bf = {0};
    bf.filesz         = 2 + sizeof(bmpfile_header) + sizeof_bmpheader + datasize;
    bf.creator1       = 0xADDE;
    bf.creator2       = 0xEFBE;
    bf.bmp_offset     = 2 + sizeof(bmpfile_header) + sizeof_bmpheader;
    return bf;
}

BITMAPINFOHEADER make_bih(uint32_t width, uint32_t height, uint16_t bpp) {
    BITMAPINFOHEADER b = {0};
    b.header_sz        = sizeof(b);
    b.width            = width;
    b.height           = height;
    b.nplanes          = 1;
    b.bitspp           = bpp;
    b.compress_type    = 0;                       /* BI_RGB */
    b.bmp_bytesz       = (width*height*bpp);
    b.hres             = PIXELS_PER_METER;
    b.vres             = PIXELS_PER_METER;
    b.ncolors          = 0;
    b.nimpcolors       = 0;
    return b;
}

BITMAPV4HEADER make_bv4(
    uint32_t width,  uint32_t height, uint16_t bpp,
    uint32_t a_mask, uint32_t r_mask, uint32_t g_mask, uint32_t b_mask
) {
    BITMAPV4HEADER b   = {0};
    b.bV4Size          = sizeof(b);
    b.bV4Width         = width;
    b.bV4Height        = height;
    b.bV4Planes        = 1;
    b.bV4BitCount      = bpp;
    b.bV4V4Compression = 3;                       /* BI_BITFIELDS */
    b.bV4SizeImage     = (height*width*bpp);
    b.bV4XPelsPerMeter = PIXELS_PER_METER;
    b.bV4YPelsPerMeter = PIXELS_PER_METER;
    b.bV4AlphaMask     = a_mask;
    b.bV4RedMask       = r_mask;
    b.bV4GreenMask     = g_mask;
    b.bV4BlueMask      = b_mask;    
    b.bV4CSType        = 0x57696E20;              /* LCS_WINDOWS_COLOR_SPACE */
    return b;
}

void write_all(bmpfile_header* bf, void* header_data, size_t header_len) {
    fprintf(stdout, "BM");
    fwrite(bf, sizeof(bmpfile_header), 1, stdout);
    fwrite(header_data, header_len, 1, stdout);
}

/* Entry point */

int main(int argc, char* argv[]) {

    /* Validate and parse parameters */
    
    if (argc != 4) {
        puts("Usage: ./bmphdr width height [32|16|16-565]\n");
        return -1;
    }
    int width = atoi(argv[1]);
    int height = atoi(argv[2]);
    
    /* Check format */
    
    if (0) {    
    } else if (! strcmp(argv[3], "32")) {    
    
        /* 32bpp, standard header */
        
        bmpfile_header bf  = make_bfh(width * height * 32, sizeof(BITMAPINFOHEADER));
        BITMAPINFOHEADER b = make_bih(width, height, 32);        
        write_all(&bf, &b, sizeof(BITMAPINFOHEADER));
    
    } else if (! strcmp(argv[3], "16")) {
    
        /* 16bpp, standard header */
        
        bmpfile_header bf  = make_bfh(width * height * 16, sizeof(BITMAPINFOHEADER));
        BITMAPINFOHEADER b = make_bih(width, height, 16);        
        write_all(&bf, &b, sizeof(BITMAPINFOHEADER));
    
    } else if (! strcmp(argv[3], "16-565")) {
    
        /* 16bpp with bitmask (v4 header) */
        
        bmpfile_header bf = make_bfh(width*height*16, sizeof(BITMAPV4HEADER));
        BITMAPV4HEADER b  = make_bv4(width, height, 16, 0xF800, 0x07E0, 0x001F, 0);
        write_all(&bf, &b, sizeof(BITMAPV4HEADER));
    
    } else {
    
        /* Unrecoginsed format */
        
        fprintf(stderr, "Unrecognised format: '%s'\n", argv[3]);
        return -1;
        
    }
    
    return 0;
}