/* $Id$ */

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

static ppm_pixel_t *alloc_data(ppm_size_t width, ppm_size_t height)
{
	ppm_pixel_t *data;

	if (width && height)
		data = (ppm_pixel_t *) calloc(width * height, sizeof(ppm_pixel_t));
	else
		data = NULL;

	return data;
}

ppm_t *ppm_alloc(ppm_size_t width, ppm_size_t height)
{
	ppm_t *ppm = (ppm_t *) malloc(sizeof(ppm_t));
	if (!ppm)
		return NULL;

	ppm->data = alloc_data(width, height);
	if (width && height && !ppm->data) {
		free(ppm);
		return NULL;
	}

	ppm->width = width;
	ppm->height = height;

	return ppm;
}

static int get_string(FILE *fp, char *buf, size_t bufsz)
{
	if (!fgets(buf, bufsz, fp))
		return -1;

	if (strlen(buf) > 0 && buf[strlen(buf) - 1] == 0x0A)
		buf[strlen(buf) - 1] = 0;

	if (strlen(buf) > 0 && buf[strlen(buf) - 1] == 0x0D)
		buf[strlen(buf) - 1] = 0;

	return 0;
}

ppm_t *ppm_load(const char *path)
{
	ppm_t *ppm;
	FILE *fp = stdin;
	char buf[64];
	ppm_size_t width, height;

	ppm = ppm_alloc(0, 0);
	if (!ppm)
		return NULL;

	if (path) {
		fp = fopen(path, "rb");
		if (!fp) {
			ppm_free(ppm);
			return NULL;
		}
	}

	if (get_string(fp, buf, sizeof(buf)) || strcmp(buf, "P6"))
		goto err;

	if (get_string(fp, buf, sizeof(buf)) || sscanf(buf, "%lu %lu", &width, &height) != 2)
		goto err;

	if (get_string(fp, buf, sizeof(buf)) || strcmp(buf, "255"))
		goto err;

	ppm->data = alloc_data(width, height);
	if (width && height && !ppm->data)
		goto err;

	ppm->width = width;
	ppm->height = height;

	if (ppm->data && fread(ppm->data, sizeof(*ppm->data), ppm->width * ppm->height, fp) != ppm->width * ppm->height)
		goto err;

	if (fp != stdin)
		fclose(fp);

	return ppm;

err:
	if (fp != stdin)
		fclose(fp);

	ppm_free(ppm);
	return NULL;
}

int ppm_save(const ppm_t *ppm, const char *path)
{
	FILE *fp = stdout;

	if (path) {
		fp = fopen(path, "wb");
		if (!fp)
			return -1;
	}

	if (fprintf(fp, "P6\n%lu %lu\n255\n", ppm->width, ppm->height) < 0)
		goto err;

	if (ppm->data && fwrite(ppm->data, sizeof(*ppm->data), ppm->width * ppm->height, fp) != ppm->width * ppm->height)
		goto err;

	if (fp != stdout)
		fclose(fp);

	return 0;

err:
	if (fp != stdout)
		fclose(fp);

	return -1;
}

void ppm_free(ppm_t *ppm)
{
	if (!ppm)
		return;

	free(ppm->data);
	free(ppm);
}
