/*

    To my parents
    Eduard Svjatoslavovich and Ludmila Aleksandrovna Barkhatov

        (c) Barkhatov Andrej Eduardovich

*/

#include "placer.h"

int tif_scan(tiftag_t **tiftag, int **cnt, unsigned char *buf)
{
	char i_m;
	int i = 0, j, k = 0, off, *c;
	tiftag_t *tt;

	i_m = *buf;
	off = SWAP_4(buf + 4, i_m);
	while (off) {
		i += j = SWAP_2(buf + off, i_m);
		off = SWAP_4(buf + off + 2 + 12 * j, i_m);
		k++;
	}
	if (!(*cnt = c = (int *) malloc(k * sizeof(int)))
			|| !(*tiftag = tt = (tiftag_t *) malloc(i * sizeof(tiftag_t))))
		return 0;
	memset(c, 0, sizeof(k * sizeof(int)));
	memset(tt, 0, i * sizeof(tiftag_t));
	off = SWAP_4(buf + 4, i_m);
	for (i = 0; i < k; i++) {
		c[i] = SWAP_2(buf + off, i_m);
		for (j = 0; j < c[i]; j++, tt++) {
			tt->tag = SWAP_2(buf + off + 2 + 12 * j, i_m);
			tt->type = SWAP_2(buf + off + 4 + 12 * j, i_m);
			tt->len = SWAP_4(buf + off + 6 + 12 * j, i_m);
			tt->val = tt->type == 3 && tt->len == 1 ? SWAP_2(buf + off + 10 + 12 * j, i_m) :
			SWAP_4(buf + off + 10 + 12 * j, i_m);
		}
		if (i != k - 1)
			off = SWAP_4(buf + off + 2 + 12 * c[i], i_m);
	}
	return k;
}

int	tif_img_read(unsigned char *buf, tiftag_t *tt, int n, IplImage **img)
{
	int	ret = 0, strp_n = 0, strp_row = 0, bp = 0, bp1 = 3;
	int *strp_map = 0, *strp_sz = 0;
	unsigned char *b0, palette[256][3];
	char *b;
	CvSize img_sz = {
		0,0																				};
	char i_m;
	int	i, j, k, m;
	IplImage *pimg;

	i_m = *(char *) buf;
	for (i = 0; i < n; i++, tt++) {
		switch (tt->tag) {
		case TIF_BIT_S:
			if (tt->len != 1 && tt->len != 3 || tt->len == 1 && tt->val != 8
					|| tt->len == 3 && (SWAP_2(buf + tt->val, i_m) != 8
					|| SWAP_2(buf + tt->val + 2, i_m) != 8 || SWAP_2(buf + tt->val + 4, i_m) != 8)) {
				ERROR_MSG("TIF", "Bits/Sample");
				goto l;
			}
			bp = tt->len == 1 ? 1 : 3;
			break;
		case TIF_W:
			if (tt->val <= 0 || tt->val > 50000) {
				ERROR_MSG("TIF", "Width");
				goto l;
			}
			img_sz.width = tt->val;
			break;
		case TIF_H:
			if (tt->val <= 0 || tt->val > 50000) {
				ERROR_MSG("TIF", "Height");
				goto l;
			}
			img_sz.height = tt->val;
			break;
		case TIF_SAM_P:
			if (tt->val != 1 && tt->val != 3) {
				ERROR_MSG("TIF", "Sample/Pix");
				goto l;
			}
			break;
		case TIF_PHOT:
			if (tt->val != 2)
				bp1 = 1;
			break;
		case TIF_CMP:
			if (tt->val != 1) {
				ERROR_MSG("TIF", "Compression");
				goto l;
			}
			break;
		case TIF_SP_OFF:
			strp_n = tt->len;
			if (!(strp_map = (int *) malloc(sizeof(int) * strp_n)))
				goto l;
			memset(strp_map, 0, sizeof(int) * strp_n);
			if (tt->len == 1)
				strp_map[0] = tt->val;
			else {
				if (tt->type == 3)
					for (j = 0; j < strp_n; j++)
						strp_map[j] = SWAP_2(buf + tt->val + 2 * j, i_m);
				if (tt->type == 4)
					for (j = 0; j < strp_n; j++)
						strp_map[j] = SWAP_4(buf + tt->val + 4 * j, i_m);
			}
			break;
		case TIF_C_MAP:
			bp1 = 3;
			k = min(256, tt->len / 3);
			for (j = 0; j < k; j++) {
				palette[j][0] = ((SWAP_2(buf + tt->val + 2 * j,         i_m)) >> 8) & 0xFF;
				palette[j][1] = ((SWAP_2(buf + tt->val + 2 * j + 2 * k, i_m)) >> 8) & 0xFF;
				palette[j][2] = ((SWAP_2(buf + tt->val + 2 * j + 4 * k, i_m)) >> 8) & 0xFF;
			}
			break;
		case TIF_ROW_SP:
			strp_row = tt->val;
			break;
		case TIF_SP_CNT:
			if (strp_n && strp_n != tt->len)
				goto l;
			if (!(strp_sz = (int *) malloc(sizeof(int) * strp_n)))
				goto l;
			memset(strp_sz, 0, sizeof(int) * strp_n);
			if (tt->len == 1)
				strp_sz[0] = tt->val;
			else {
				if (tt->type == 3)
					for (j = 0; j < strp_n; j++)
						strp_sz[j] = SWAP_2(buf + tt->val + 2 * j, i_m);
				if (tt->type == 4)
					for (j = 0; j < strp_n; j++)
						strp_sz[j] = SWAP_4(buf + tt->val + 4 * j, i_m);
			}
			break;
		}
	}
	if (!bp || !img_sz.width || !img_sz.height) {
		ERROR_MSG("TIF", "Invalid");
		goto l;
	}
	if (!(pimg = *img = cvCreateImage(img_sz, IPL_DEPTH_8U, bp1)))
		goto l;
	b = pimg->imageData;
	b0 = buf + strp_map[0];
	if (strp_n) {
		int pad = pimg->widthStep - bp1 * img_sz.width;
		m = 0;
		k = strp_sz[0];
		for (i = 0; i < img_sz.height; i++) {
			j = bp * img_sz.width;
			while (j) {
				int i0 = min(k, j);
				if (bp1 == 3 && bp == 1) {
					int i1;
					for (i1 = 0; i1 < i0; i1++, b0++, b += 3)
						memcpy(b, palette[*b0], 3);
				}
				else  {
					memcpy(b, b0, i0);
					b0 += i0;
					b += i0;
				}
				k -= i0;
				j -= i0;
				if (!k) {
					if (m == strp_n - 1)
						break;
					k = strp_sz[++m];
					b0 = buf + strp_map[m];
				}
			}
			b += pad;
		}
	}
	else {
		for (i = 0; i < img_sz.height; i++, b += pimg->widthStep, b0 += img_sz.width * bp) {
			if (bp1 == 3 && bp == 1)
				for (m = 0; m < img_sz.width; m++)
					memcpy(b + 3 * m, palette[*(b0 + m)], 3);
			else
				memcpy(b, b0, img_sz.width * bp);
		}
	}
	ret = 1;
l:
	if (strp_map)
		free(strp_map);
	return ret;
}

int tif_read(char *name, IplImage **img)
{
	unsigned char *b = 0, i_m;
	int *c = 0, ret = 0, i, x, n;
	tiftag_t *tt = 0, *ptt;
	size_t sz;

	BREAD(name, &sz, &b);

	if (!b)
		goto l;
	if (((i_m = *b) != 'I' && i_m != 'M') || SWAP_2(b + 2, i_m) != 42)
		goto l;
	if (!(n = tif_scan(&tt, &c, b)))
		goto l;
	for (i = 0, ptt = tt, x = 0; i < 1; ptt += c[i++]) {
		if (!tif_img_read(b, ptt, c[i], img))
			goto l;
		if ((*img)->nChannels == 3)
			SWAP_BUF((*img)->imageData, (*img)->width, (*img)->height, (*img)->widthStep - 3 * (*img)->width);
	}
	ret = 1;
l:
	if (b)
		free(b);
	if (c)
		free(c);
	if (tt)
		free(tt);
	return ret;
}
