/**
 * $Id:$
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * The contents of this file may be used under the terms of either the GNU
 * General Public License Version 2 or later (the "GPL", see
 * http://www.gnu.org/licenses/gpl.html ), or the Blender License 1.0 or
 * later (the "BL", see http://www.blender.org/BL/ ) which has to be
 * bought from the Blender Foundation to become active, in which case the
 * above mentioned GPL option does not apply.
 *
 * The Original Code is Copyright (C) 2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

#include "imbuf.h"


#define OBJECTBLOK "tim"

#define GLS(x) (((uchar *)(x))[3] << 24 | ((uchar *)(x))[2] << 16 | ((uchar *)(x))[1] << 8 | ((uchar *)(x))[0])

void apply_tim_cmap(uint * data, uint count) {
	int col, a, b, g, r, cur;
	
	for (cur = 0; cur < count; cur++) {
		col = *data;
		
		a = col >> 15;
		b = (col >> 10) & 0x1f;
		g = (col >> 5) & 0x1f;
		r = col & 0x1f;
		
		/*if (count < 257) printf("%4d: %d.%2d.%2d.%2d\n", cur, a, b, g, r);*/

		if (col == 0) {
			a = 0;
		} else if (a == 0) {
			a = 255;
		} else {
			a = 128;
		}

		b = (b << 3) | (b >> 2);
		g = (g << 3) | (g >> 2);
		r = (r << 3) | (r >> 2);
			
		*data++ = (a << 24) | (b << 16) | (g << 8) | r;
		/*if (count < 257) printf("0x%08x\n", data[-1]);*/
	}
}

void to_tim_cmap(uint * data, uint count) {
	int col, a, b, g, r, cur;
	
	for (cur = 0; cur < count; cur++) {
		col = *data;
		
		a = col >> 24;
		b = (col >> 19) & 0x1f;
		g = (col >> 11) & 0x1f;
		r = (col >> 3) & 0x1f;
		
		/*if (count < 257) printf("%4d: %d.%2d.%2d.%2d ", cur, a, b, g, r);*/
		
		/* zwart = 100 % transparant
		 * dus niet 100% transparant is NIET zwart
		 */
		
		if (a == 0) b = g = r = 0;
		else a = 255;
		
		/* alpha afhandelen, een mogelijkheid */
		
/*		if (a < 64) {
			a = b = g = r = 0;
		} else if (a < 192) {
			a = 1;
		} else {
			a = 0;
		}
*/
		*data++ = ((a & 0x80) << 8) | (b << 10) | (g << 5) | r;
	}
}

struct ImBuf *loadtim(ushort * mem, int flags)
{
	TARGA tga;
	struct ImBuf * ibuf;
	int count, size, type, clut, x, y, col, depth;
	uint * rect;
	uchar * cmem;
	
	if (mem == 0) return(0);
	
	/*printf("%x %x\n", GLS(mem),  GLS(mem + 2));*/
	type = GLS(mem + 2);
	clut = type & TIM_CLUT;
	type |= TIM;
	
	switch (type) {
		case TIM_4:
			depth = 4;
			break;
		case TIM_8:
			depth = 8;
			break;
		case TIM_16:
			depth = 16;
			break;
		case TIM_24:
			depth = 24;
			break;			
	}
	
	ibuf = allocImBuf(0, 0, depth, 0, 0);
	if (ibuf == 0) return(0);

	ibuf->ftype = type;
	
	mem += 4;

	if (clut) {
		size = GLS(mem);
		mem += 6;	/* ignore x,y and w,h for now */
		
		size -= 12;
		size /= 2;
		ibuf->mincol = 0;
		ibuf->maxcol = size;
		
		addcmapImBuf(ibuf);
		
		for (count = 0; count < size; count++) {
			ibuf->cmap[count] = GSS(mem);
			mem++;
		}
		apply_tim_cmap(ibuf->cmap, size);
	}
	
	size = GLS(mem); mem += 2; size -= 12;
	ibuf->xorig = GSS(mem); mem++;
	ibuf->yorig = GSS(mem); mem++;
	ibuf->x = GSS(mem); mem++;
	ibuf->y = GSS(mem); mem++;
	
	switch(ibuf->ftype) {
		case TIM_4:
			ibuf->x *= 4;
			break;
		case TIM_8:
			ibuf->x *= 2;
			break;
		case TIM_24:
			ibuf->x = (ibuf->x * 2) / 3;
			break;
	}
	
	/*printf("%d %d at %d %d\n", ibuf->x, ibuf->y, ibuf->xorig, ibuf->yorig);*/
	if (flags & IB_test) return(ibuf);
	
	addrectImBuf(ibuf);
	
	rect = ibuf->rect;
	size /= 2;
	
	switch (ibuf->ftype) {
		case TIM_4:
			for (count = 0; count < size; count++) {
				col  = GSS(mem); mem++;
				*rect++ = col & 0xf;
				*rect++ = (col >> 4) & 0xf;
				*rect++ = (col >> 8) & 0xf;
				*rect++ = (col >> 12) & 0xf;
			}
			if ((flags & IB_cmap) == 0) applycmap(ibuf);
			break;
		case TIM_8:
			for (count = 0; count < size; count++) {
				col  = GSS(mem); mem++;
				*rect++ = col & 0xff;
				*rect++ = (col >> 8) & 0xff;				
			}
			if ((flags & IB_cmap) == 0) applycmap(ibuf);
			break;
		case TIM_16:
			for (count = 0; count < size; count++) {
				*rect++ = GSS(mem); mem++;
			}
			if ((flags & IB_cmap) == 0) apply_tim_cmap(ibuf->rect, size);
			break;
		case TIM_24:
			size *= 2;
			size /= 3;
			cmem = (uchar *) mem;
			for (count = 0; count < size; count++) {
				*rect++ = cmem[0] + (cmem[1] <<  8) + (cmem[2] << 16);
				cmem += 3;
			}
			break;
	}
	if ((flags & IB_ttob) == 0) flipy(ibuf);

	if (ibuf) {
		if (ibuf->rect) 
			convert_rgba_to_abgr((ibuf->x+ibuf->skipx)*ibuf->y, ibuf->rect);
	}
	
	return(ibuf);
}

int write_tim_short_data(int file, uint * data, uint count)
{
	int i, ok = TRUE;
	short * _mem, * mem;

	to_tim_cmap(data, count);
	mem = _mem = malloc(count * sizeof(short));
	
	for (i = 0; i < count; i++) {
		*mem++ = GSS(((char *) data) + 2);
		data++;
	}
	
	if (write(file, _mem, count * sizeof(short)) != count * sizeof(short)) ok = FALSE;

	free(_mem);
	return (ok);
}

short savetim(struct ImBuf * ibuf, int file, int flags)
{
	int buf[32], size, ok = TRUE;
	short x, y, *_mem, *mem, col;
	char * cmem;
	uint * rect, lcol;
	
	/*
		verschillende manieren om het alpha bitje van een TIM te gebruiken:
		aan -> geen color 0
		optellen / aftrekken -> overal alpha bit
		aan / uit -> color 0
		antialias -> color 0, alphabit transparant
		
		Plaatjes met meerdere colormaps worden nog even niet ondersteund
	*/
	
	if (ibuf->ftype == TIM) {
		/* automatiese type bepaling */
		if (ibuf->cmap) {
			if (ibuf->depth == 4) ibuf->ftype = TIM_4;
			else if (ibuf->depth == 8) ibuf->ftype = TIM_8;
			else if (ibuf->maxcol <= 16) ibuf->ftype = TIM_4;
			else if (ibuf->maxcol <= 256) ibuf->ftype = TIM_8;
			else {
				printf("To many colors (%d) for colormapped tim.\nIgnoring cmap.\n");
				ibuf->ftype = TIM_16;
				freecmapImBuf(ibuf);
			}
		} else {
			/* for now */
			ibuf->ftype = TIM_16;
		}
	}
	
	buf[0] = 0x10000000;
	buf[1] = ibuf->ftype << 24;
	
	if (write(file, buf, 8) != 8) return(0);
	
	if (ibuf->ftype & TIM_CLUT) {
		if ((flags & IB_cmap) == 0) converttocmap(ibuf);
		
		size = (2 * ibuf->maxcol) + 12;
		buf[0] = GLS(&size);
		buf[1] = 0;	/* nog geen x en y */
		buf[2] = 0x01000000 | (GSS(&ibuf->maxcol));
		
		if (write(file, buf, 12) != 12) return(0);
		if (write_tim_short_data(file, ibuf->cmap, ibuf->maxcol) == FALSE) return(0);
	}
	
	if ((flags & IB_ttob) == 0) flipy(ibuf);
	
	x = ibuf->x;
	y = ibuf->y;
	switch (ibuf->ftype) {
		case TIM_4:
			if (x & 0x3) printf("TIM_4 width not multiple of 4: cropping\n");
			x /= 4;
			break;
		case TIM_8:
			if (x & 0x1) printf("TIM_8 width not multiple of 2: cropping\n");
			x /= 2;
			break;
		case TIM_16:
			break;
		case TIM_24:
			if (x & 0x1) printf("TIM_24 width not multiple of 2: cropping\n");
			x &= ~1;
			x *= 1.5;
			break;
	}
	
	size = (x * y * 2) + 12;
	buf[0] = GLS(&size);
	buf[1] = 0;
	buf[2] = GSS(&x) << 16 | GSS(&y);
	
	if (write(file, buf, 12) != 12) return(0);
	
	size = x * sizeof(short);
	_mem = malloc(size);
	
	switch (ibuf->ftype) {
		case TIM_4:
			for (y = 0; y < ibuf->y; y++) {
				rect = ibuf->rect + (y * ibuf->x);
				mem = _mem;
				for (x = ibuf->x / 4; x > 0; x--) {
					col = (rect[0] & 0xf) | ((rect[1] & 0xf) << 4) | ((rect[2] & 0xf) << 8) | ((rect[3] & 0xf) << 12);
					*mem++ = GSS(&col);
					rect += 4;
				}
				if (write(file, _mem, size) != size) {
					ok = FALSE;
					break;
				}
			}
			break;
		case TIM_8:
			for (y = 0; y < ibuf->y; y++) {
				rect = ibuf->rect + (y * ibuf->x);
				mem = _mem;				
				for (x = ibuf->x / 2; x > 0; x--) {
					
					col = (rect[1] & 0xff) | ((rect[0] & 0xff) << 8);
					*mem++ = col;
					rect += 2;
				}
				if (write(file, _mem, size) != size) {
					ok = FALSE;
					break;
				}
			}
			break;
		case TIM_16:
			ditherfunc(ibuf,1,5);
			ditherfunc(ibuf,2,5);
			ditherfunc(ibuf,3,5);

			ok = write_tim_short_data(file, ibuf->rect, x * y);
			break;
		case TIM_24:
			size = ibuf->x * 3;
			for (y = 0; y < ibuf->y; y++) {
				rect = ibuf->rect + (y * ibuf->x);
				cmem = (char *) _mem;
				
				for (x = (ibuf->x & ~1); x > 0; x--) {
					lcol = *rect++;
					*cmem++ = lcol;
					*cmem++ = lcol >> 8;
					*cmem++ = lcol >> 16;
				}
				
				if (write(file, _mem, size) != size) {
					ok = FALSE;
					break;
				}
			}
			break;
	}
	
	free(_mem);
	
	return (ok);
}