/*
 * $Id$
 *
 * $Date$
 * $Revision$
 *
 * (C) 1999 by Hyperion
 * All rights reserved
 *
 * This file is part of the MiniGL library project
 * See the file Licence.txt for more details
 *
 */

#include "displaylists.h"
#include "sysinc.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

extern char *format_to_string(GLenum format);
extern char *type_to_string(GLenum type);

static inline uint16 load_16_little(uint16 *addr)
{
        uint16 x;
        __asm volatile ("lhbrx %0, 0, %1" : "=r" (x) : "r" (addr));
        return x;
}

static inline void store_16_little(uint16 *addr, uint16 val)
{
        __asm volatile ("sthbrx %0, 0, %1" :: "r" (val), "r" (addr));
}

static inline uint32 load_32_little(uint32 *addr)
{
        uint32 x;
        __asm volatile ("lwbrx %0, 0, %1" : "=r" (x) : "r" (addr));
        return x;
}

static inline void store_32_little(uint32 *addr, uint32 val)
{
        __asm volatile ("stwbrx %0, 0, %1" :: "r" (val), "r" (addr));
}


#define R_COMPONENT	0
#define G_COMPONENT 1
#define B_COMPONENT 2
#define A_COMPONENT 3

static void _pack_rgb_b(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint8 *from = (uint8 *)src;
	uint8 *to = (uint8 *)dst->current;

	/* Note: swap_bytes not applicable to three-byte pixels */
	while (num_pixels--)
	{
		*to++ = from[R_COMPONENT];
		*to++ = from[G_COMPONENT];
		*to++ = from[B_COMPONENT];

		from += 4;
	}
}

static void _pack_rgba_b(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint32 *from = (uint32 *)src;
	uint32 *to = (uint32 *)dst->current;

	if (pack->swap_bytes)
	{
		while (num_pixels--)
			*to++ = load_32_little(from++);
	}
	else
	{
		while (num_pixels--)
			*to++ = *from++;
	}
}


static void _pack_argb_b(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint32 *from = (uint32 *)src;
	uint32 *to = (uint32 *)dst->current;

	/* Note: assumes BGRA byte reversed */
	if (!pack->swap_bytes)
	{
		while (num_pixels--)
			*to++ = load_32_little(from++);
	}
	else
	{
		while (num_pixels--)
			*to++ = *from++;
	}
}


static void _pack_a_b(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint8 *from = (uint8 *)src;
	uint8 *to = (uint8 *)dst->current;

	while (num_pixels--)
	{
		*to++ = from[A_COMPONENT];

		from += 4;
	}
}

static void _pack_l_b(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint8 *from = (uint8 *)src;
	uint8 *to = (uint8 *)dst->current;

	while (num_pixels--)
	{
		uint32 l = from[R_COMPONENT] + from[G_COMPONENT] + from[B_COMPONENT];
		if (l > 255)
			l = 255;

		*to++ = l;
		from += 4;
	}
}


static void _pack_la_b(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint8 *from = (uint8 *)src;
	uint8 *to = (uint8 *)dst->current;

	if (pack->swap_bytes)
	{
		while (num_pixels--)
		{
			uint32 l = from[R_COMPONENT] + from[G_COMPONENT] + from[B_COMPONENT];
			if (l > 255)
				l = 255;

			*to++ = from[A_COMPONENT];
			*to++ = l;

			from += 4;
		}
	}
	else
	{
		while (num_pixels--)
		{
			uint32 l = from[R_COMPONENT] + from[G_COMPONENT] + from[B_COMPONENT];
			if (l > 255)
				l = 255;

			*to++ = l;
			*to++ = from[A_COMPONENT];

			from += 4;
		}
	}
}


static void _pack_i_b(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint8 *from = (uint8 *)src;
	uint8 *to = (uint8 *)dst->current;

	while (num_pixels--)
	{
		*to++ = from[R_COMPONENT];
		from += 4;
	}
}

static void _pack_rgb_us565(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint8 *from = (uint8 *)src;
	uint16 *to = (uint16 *)dst->current;

	if (pack->swap_bytes)
	{
		while (num_pixels--)
		{
			store_16_little(to, ((from[R_COMPONENT] & 0xf8) << 8)
					   		  | ((from[G_COMPONENT] & 0xfc) << 3)
					   		  | ((from[B_COMPONENT] & 0xf8) >> 3));
			from += 4;
			to++;
		}
	}
	else
	{
		while (num_pixels--)
		{
			*to++ = ((from[R_COMPONENT] & 0xf8) << 8)
		 		  | ((from[G_COMPONENT] & 0xfc) << 3)
		   		  | ((from[B_COMPONENT] & 0xf8) >> 3);
			from += 4;
		}
	}
}



static void _pack_rgb_us565r(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint8 *from = (uint8 *)src;
	uint16 *to = (uint16 *)dst->current;

	if (pack->swap_bytes)
	{
		while (num_pixels--)
		{
			store_16_little(to, ((from[B_COMPONENT] & 0xf8) << 8)
					   		  | ((from[G_COMPONENT] & 0xfc) << 3)
					   		  | ((from[R_COMPONENT] & 0xf8) >> 3));
			from += 4;
			to++;
		}
	}
	else
	{
		while (num_pixels--)
		{
			*to++ = ((from[B_COMPONENT] & 0xf8) << 8)
		 		  | ((from[G_COMPONENT] & 0xfc) << 3)
		   		  | ((from[R_COMPONENT] & 0xf8) >> 3);
			from += 4;
		}
	}
}

static void _pack_rgba_us4444(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint8 *from = (uint8 *)src;
	uint16 *to = (uint16 *)dst->current;

	if (pack->swap_bytes)
	{
		while (num_pixels--)
		{
			store_16_little(to, ((from[R_COMPONENT] & 0xf0) << 8)
					   		  | ((from[G_COMPONENT] & 0xf0) << 4)
					   		  | ((from[B_COMPONENT] & 0xf0)     )
					   		  | ((from[A_COMPONENT] & 0xf0) >> 4));
			from += 4;
			to++;
		}
	}
	else
	{
		while (num_pixels--)
		{
			*to++ = ((from[R_COMPONENT] & 0xf0) << 8)
	   		  	  | ((from[G_COMPONENT] & 0xf0) << 4)
	   		  	  | ((from[B_COMPONENT] & 0xf0)     )
	   		  	  | ((from[A_COMPONENT] & 0xf0) >> 4);
			from += 4;
		}
	}
}


static void _pack_rgba_us4444r(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint8 *from = (uint8 *)src;
	uint16 *to = (uint16 *)dst->current;

	if (pack->swap_bytes)
	{
		while (num_pixels--)
		{
			store_16_little(to, ((from[A_COMPONENT] & 0xf0) << 8)
					   		  | ((from[B_COMPONENT] & 0xf0) << 4)
					   		  | ((from[G_COMPONENT] & 0xf0)     )
					   		  | ((from[R_COMPONENT] & 0xf0) >> 4));
			from += 4;
			to++;
		}
	}
	else
	{
		while (num_pixels--)
		{
			*to++ = ((from[A_COMPONENT] & 0xf0) << 8)
	   		  	  | ((from[B_COMPONENT] & 0xf0) << 4)
	   		  	  | ((from[G_COMPONENT] & 0xf0)     )
	   		  	  | ((from[R_COMPONENT] & 0xf0) >> 4);
			from += 4;
		}
	}
}


static void _pack_rgba_us1555(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint8 *from = (uint8 *)src;
	uint16 *to = (uint16 *)dst->current;

	if (pack->swap_bytes)
	{
		while (num_pixels--)
		{
			store_16_little(to, ((from[A_COMPONENT] & 0x80) << 8)
							  | ((from[R_COMPONENT] & 0xf8) << 7)
					   		  | ((from[G_COMPONENT] & 0xf8) << 2)
					   		  | ((from[B_COMPONENT] & 0xf8) >> 3));
			from += 4;
			to++;
		}
	}
	else
	{
		while (num_pixels--)
		{
			*to++ = ((from[A_COMPONENT] & 0x80) << 8)
			      | ((from[R_COMPONENT] & 0xf8) << 7)
			      | ((from[G_COMPONENT] & 0xf8) << 2)
			      | ((from[B_COMPONENT] & 0xf8) >> 3);
			from += 4;
		}
	}
}

static void _pack_rgba_us1555r(GLcontext context, int num_pixels, void *src, GLimage_info *dst,
		GLpixel_store *pack)
{
	uint8 *from = (uint8 *)src;
	uint16 *to = (uint16 *)dst->current;

	if (pack->swap_bytes)
	{
		while (num_pixels--)
		{
			store_16_little(to, ((from[A_COMPONENT] & 0x80) >> 7)
							  | ((from[B_COMPONENT] & 0xf8) << 8)
					   		  | ((from[G_COMPONENT] & 0xf8) << 3)
					   		  | ((from[R_COMPONENT] & 0xf8) >> 2));
			from += 4;
			to++;
		}
	}
	else
	{
		while (num_pixels--)
		{
			*to++ =((from[A_COMPONENT] & 0x80) >> 7)
			     | ((from[B_COMPONENT] & 0xf8) << 8)
	   		  	 | ((from[G_COMPONENT] & 0xf8) << 3)
	   		  	 | ((from[R_COMPONENT] & 0xf8) >> 2);
			from += 4;
		}
	}
}

GLpack mglPackers[] =
{
	{GL_RGB,				GL_UNSIGNED_BYTE,				_pack_rgb_b,		3},
	{3,						GL_UNSIGNED_BYTE,				_pack_rgb_b,		3},
	{GL_RGBA,				GL_UNSIGNED_BYTE,				_pack_rgba_b,		4},
	{4,						GL_UNSIGNED_BYTE,				_pack_rgba_b,		4},
	/* Note: BGRA with unsigned int reversed is ARGB */
	{GL_BGRA,				GL_UNSIGNED_INT_8_8_8_8_REV,	_pack_argb_b,		4},
	{GL_ALPHA,				GL_UNSIGNED_BYTE,				_pack_a_b,			1},
	{GL_LUMINANCE,			GL_UNSIGNED_BYTE,				_pack_l_b,			1},
	{1,						GL_UNSIGNED_BYTE,				_pack_l_b,			1},
	{GL_LUMINANCE_ALPHA,	GL_UNSIGNED_BYTE,				_pack_la_b,			2},
	{2,						GL_UNSIGNED_BYTE,				_pack_la_b,			2},
	{GL_INTENSITY,			GL_UNSIGNED_BYTE,				_pack_i_b,			1},
	{GL_RGB5,				GL_UNSIGNED_SHORT_5_6_5,		_pack_rgb_us565,	2},
	{GL_RGB,				GL_UNSIGNED_SHORT_5_6_5,		_pack_rgb_us565,	2},
	{GL_RGB5,				GL_UNSIGNED_SHORT_5_6_5_REV,	_pack_rgb_us565r,	2},
	{GL_RGB,				GL_UNSIGNED_SHORT_5_6_5_REV,	_pack_rgb_us565r,	2},
	{GL_RGB4,				GL_UNSIGNED_SHORT_4_4_4_4,		_pack_rgba_us4444,	2},
	{GL_RGB,				GL_UNSIGNED_SHORT_4_4_4_4,		_pack_rgba_us4444,	2},
	{GL_RGB4,				GL_UNSIGNED_SHORT_4_4_4_4_REV,	_pack_rgba_us4444r,	2},
	{GL_RGB,				GL_UNSIGNED_SHORT_4_4_4_4_REV,	_pack_rgba_us4444r,	2},
	{GL_RGB5_A1,			GL_UNSIGNED_SHORT_5_5_5_1,		_pack_rgba_us1555,	2},
	{GL_RGB,				GL_UNSIGNED_SHORT_5_5_5_1,		_pack_rgba_us1555,	2},
	{GL_RGB5_A1,			GL_UNSIGNED_SHORT_1_5_5_5_REV,	_pack_rgba_us1555r,	2},
	{GL_RGB,				GL_UNSIGNED_SHORT_1_5_5_5_REV,	_pack_rgba_us1555r,	2},
	{GL_NONE,				GL_NONE,					NULL, 				0},
};


_glPackFn MGLSelectPacker(GLenum format, GLenum type, GLuint *pixelStride)
{
	int i = 0;

	while (mglPackers[i].format != GL_NONE)
	{
		if (format == mglPackers[i].format && type == mglPackers[i].type)
		{
			if (pixelStride)
				*pixelStride = mglPackers[i].stride;
			return mglPackers[i].pack;
		}

		i++;
	}

	IExec->DebugPrintF("Missing pack support for %s/%s\n",
			format_to_string(format), type_to_string(type));

	return NULL;
}

