#include <stdlib.h>
#define QOI_IMPLEMENTATION
#include <qoi.h>
#include <allegro5/allegro5.h>
#include <allegro5/bitmap.h>
#include <allegro5/bitmap_io.h>
#include <allegro5/bitmap_lock.h>
#include <allegro5/color.h>
#include <allegro5/file.h>

#ifdef __cplusplus
extern "C" {
#endif

ALLEGRO_BITMAP* _al_load_qoi_f(ALLEGRO_FILE* file, int flags) {
	qoi_desc meta;
	ALLEGRO_PIXEL_FORMAT fmt;
	uint8_t* file_bytes = malloc(al_fsize(file));
	memset(file_bytes, 0, al_fsize(file));
	void* file_pos = file_bytes;
	#define chunk_size 1024
	al_fseek(file, 0, SEEK_SET);
	while (!al_feof(file))
		file_pos += al_fread(file, file_pos, chunk_size);

	void* data = qoi_decode(file_bytes, al_fsize(file), &meta, 0);
	free(file_bytes); if (!data) return NULL;

	int oldflags = al_get_new_bitmap_flags();
	al_set_new_bitmap_flags(flags);
	switch (meta.channels) {
		case 3: fmt = ALLEGRO_PIXEL_FORMAT_BGR_888; break;
		case 4: fmt = ALLEGRO_PIXEL_FORMAT_ABGR_8888; break;
		default: free(data); return NULL; break;
	}
	ALLEGRO_BITMAP* bmp = al_create_bitmap(meta.width, meta.height);
	ALLEGRO_LOCKED_REGION* lock = al_lock_bitmap(
		bmp,
		fmt,
		ALLEGRO_LOCK_WRITEONLY
	);
	al_set_new_bitmap_flags(oldflags);
	if (!lock) return NULL;

	const int qoi_pitch = meta.width * meta.channels;
	for (int i = 0; i < meta.height; i++)
		memcpy(lock->data + lock->pitch*i, data + qoi_pitch*i, qoi_pitch);

	al_unlock_bitmap(bmp);
	free(data); return bmp;
}

ALLEGRO_BITMAP* _al_load_qoi(const char* filename, int flags) {
	ALLEGRO_FILE* fp;
	ALLEGRO_BITMAP* bmp;

	ALLEGRO_ASSERT(filename);

	fp = al_fopen(filename, "rb");
	if (!fp) {
		//ALLEGRO_ERROR("Unable to open %s for reading.\n", filename);
		return NULL;
	}

	bmp = _al_load_qoi_f(fp, flags);

	al_fclose(fp);
	return bmp;
}

bool _al_identify_qoi(ALLEGRO_FILE* f) {
  uint32_t magic_expected = 1718185841;
  uint32_t magic_found = al_fread32le(f);
	if (magic_found != magic_expected)
  	return false;
  if (!al_fseek(f, 14 - 4, ALLEGRO_SEEK_CUR)) // check for min size 14-byte header minus 32-bit magic
    return false;
  return true;
}

bool al_init_qoi() {
  int insane = 0;
  insane |= al_register_bitmap_loader(".qoi", _al_load_qoi);
  insane |= al_register_bitmap_loader_f(".qoi", _al_load_qoi_f);
  insane |= al_register_bitmap_identifier(".qoi", _al_identify_qoi);
  return insane;
}

#ifdef __cplusplus
}
#endif