#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "bmp.h"

uint32_t calc_bitmap_pixel_array_size_bytes(const bitmap_header *bitmap_in) {
	/* We will assume that the bitmap bits per pixel is always a multiple of 8 and sizeof byte is always 8 bits */
	uint32_t bytes_unpadded_row = ((bitmap_in->image_width  * bitmap_in->bits_per_pixel) / 8);
	uint32_t bytes_padded_row = bytes_unpadded_row;
	if (bytes_padded_row % 4) /* Ensure 4 byte alignment for every row */
		bytes_padded_row = (bytes_unpadded_row - (bytes_unpadded_row % 4)) + 4;
	return bytes_padded_row * bitmap_in->image_height;
};

uint32_t calc_bitmap_file_size_bytes(const bitmap_header *bitmap_in) {
	/* We will assume that the bitmap bits per pixel is always a multiple of 8 and sizeof byte is always 8 bits */
	return sizeof(bitmap_header) + calc_bitmap_pixel_array_size_bytes(bitmap_in) - 2 ; /* Take away two to account for compiler padding struct */
};

uint32_t calc_bitmap_row_byte_position_start(const bitmap_header *bitmap_in, const uint32_t row_in) {
	uint32_t bytes_unpadded_row = ((bitmap_in->image_width  * bitmap_in->bits_per_pixel) / 8);
	uint32_t bytes_padded_row = bytes_unpadded_row;
	if (bytes_padded_row % 4)
		bytes_padded_row = (bytes_unpadded_row - (bytes_unpadded_row % 4)) + 4;
	return (bytes_padded_row * (row_in - 1));
};

uint32_t calc_bitmap_header_size_bytes() {
	return sizeof(bitmap_header) - 2 ; /* Take away two to account for compiler padding struct */
};

bitmap_header init_bitmap_header(const int32_t image_width_in, const int32_t image_height_in) {
	bitmap_header new_bitmap;
	/* Bitmap Info Header */
	new_bitmap.info_header_size_bytes = 40;
	new_bitmap.image_width = image_width_in;
	new_bitmap.image_height = image_height_in;
	new_bitmap.color_planes_used = 1;
	new_bitmap.bits_per_pixel = 24;
	new_bitmap.compression_method = 0;
	
	new_bitmap.image_size = calc_bitmap_pixel_array_size_bytes(&new_bitmap);
	new_bitmap.horizontal_resolution_pixels_per_metre = 2835; /* Approx 72DPI */
	new_bitmap.vertical_resolution_pixels_per_metre = 2835;
	new_bitmap.colors_in_palette = 0;
	new_bitmap.important_colors = 0;

	/* File Header */
	new_bitmap.header_field[0] = 'B'; 
	new_bitmap.header_field[1] = 'M'; 
	new_bitmap.file_size_bytes = calc_bitmap_file_size_bytes(&new_bitmap);
	new_bitmap.reserved1 = 0;
	new_bitmap.reserved2 = 0;
	new_bitmap.image_data_start_offset = 54;		

	return new_bitmap;
};

bitmap_file init_bitmap_file(const bitmap_header *bitmap_in, const char *filename) {
	bitmap_file new_bitmap_file;
	int8_t write_status;
	uint32_t current_byte; 
	uint8_t blank_byte_buffer[1] = { 0 }; 
	uint8_t *bitmap_in_byte_ptr = (uint8_t *)bitmap_in + 2; /* Offset by two to account for padding */
	int8_t fd = open(filename, O_WRONLY | O_CREAT, 0666);
	if (fd == -1)
		/* TODO Need an error return type */
		return new_bitmap_file;
	/* Write Header */
	write_status = write(fd, bitmap_in_byte_ptr, calc_bitmap_header_size_bytes());
	/* Write blank pixel array */
	for (current_byte = bitmap_in->image_data_start_offset; current_byte < calc_bitmap_file_size_bytes(bitmap_in); ++current_byte) 
		write_status = pwrite(fd, blank_byte_buffer, 1, current_byte);
	if (fd == -1) {
		close(fd);
		/* TODO Need an error return type */
		return new_bitmap_file;
	}

	new_bitmap_file.filename = filename;
	new_bitmap_file.bitmap_metadata = bitmap_in;
	new_bitmap_file.fd = fd;
	return new_bitmap_file;
};

int write_bitmap_pixel(const bitmap_file *bitmap_file_in, const bitmap_pixel_color *bitmap_pixel_in, const int32_t x_in, const int32_t y_in) {
	int8_t write_status;
	int32_t byte_position_start = calc_bitmap_row_byte_position_start(bitmap_file_in->bitmap_metadata, y_in) + ((x_in - 1) * 3);
	uint8_t *bitmap_pixel_in_byte_ptr = (uint8_t *)bitmap_pixel_in;

	write_status = pwrite(bitmap_file_in->fd, bitmap_pixel_in_byte_ptr, 3, bitmap_file_in->bitmap_metadata->image_data_start_offset + byte_position_start);

	return 0;
};
