/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#include <dxconfig.h>



#include "hwDeclarations.h"
#include "hwXfield.h"
#include "hwMatrix.h"
#include "hwMemory.h"
#include "hwPortLayer.h"
#include "hwWindow.h"
#include "hwObjectHash.h"

#define String dxString
#define Object dxObject
#define Angle dxAngle
#define Matrix dxMatrix
#define Screen dxScreen
#define Boolean dxBoolean
#include "internals.h"
#undef String
#undef Object
#undef Angle
#undef Matrix
#undef Screen
#undef Boolean

#include "hwDebug.h"

typedef struct _texture
{
    int       width, height;
    ubyte     *pixels;
    dxObject  textureObj;
    int       myPixels;
} *Texture;

static Error _getTexture_Field(Texture);
static Error _getTexture_CompositeField(Texture);

static Error
_deleteTexture(Pointer p)
{
    if (p)
    {
	Texture t = (Texture)p;
	if (t->myPixels)
	    DXFree((Pointer)t->pixels);
	if (t->textureObj)
	    DXDelete(t->textureObj);
    }
    return OK;
}

static dxObject
_newTexture(dxObject obj)
{
    dxObject txo = NULL;
    Texture texture = (Texture)DXAllocateZero(sizeof(struct _texture));
    if (! texture)
	goto error;

    texture->textureObj = DXReference(obj);

    if (DXGetObjectClass(obj) == CLASS_FIELD)
    {
	if (! _getTexture_Field(texture))
	    goto error;
    }
    else if (DXGetObjectClass(obj) == CLASS_GROUP &&
	    DXGetGroupClass((Group)obj) == CLASS_COMPOSITEFIELD)
    {
	if (! _getTexture_CompositeField(texture))
	    goto error;
    }
    else
    {
	DXSetError(ERROR_INVALID_DATA, "invalid object type for texture");
	goto error;
    }

    txo = (dxObject)DXNewPrivate((Pointer)texture, _deleteTexture);
    if (! txo)
	goto error;

    return txo;

error:
    _deleteTexture((Pointer)texture);
    return ERROR;
}

Error
_XTexture(Field f, xfieldT* xf, void *globals)
{
    DEFGLOBALDATA(globals);
    Type  type;
    Category cat;
    int   n, rank, shape[32], counts[2];
    dxObject attr, texture = _dxf_xfieldAttributes(xf)->texture;
    dxObject txo;

    if (texture)
    {
	Texture txp;

	xf->uv_array = (Array)DXGetComponentValue(f, "uv");
	if (! xf->uv_array)
	{
	    DXSetError(ERROR_INVALID_DATA,
		"texture attribute but no uv component");
	    goto error;
	}

	DXGetArrayInfo(xf->uv_array, NULL, &type, NULL, &rank, shape);

	if (type != TYPE_FLOAT || rank != 1 || shape[0] != 2)
	{
	    DXSetError(ERROR_INVALID_DATA, 
		"uv component must contain float 2-vectors");
		goto error;
	}

	attr = DXGetAttribute((dxObject)xf->uv_array, "dep");
	if (DXGetObjectClass(attr) != CLASS_STRING)
	{
	    DXSetError(ERROR_INVALID_DATA, 
		"uv dep attribute must be a DXString object");
	    goto error;
	}

	if (strcmp(DXGetString((dxString)attr), "positions"))
	{
	    DXSetError(ERROR_INVALID_DATA, 
		"uv must be dep on positions");
	    goto error;
	}

	xf->uv = DXCreateArrayHandle(xf->uv_array);
	if (! xf->uv)
	    goto error;
    
	DXReference((dxObject)xf->uv_array);

	txo = _dxf_QueryObject(TEXTUREHASH, texture);
	if (! txo)
	{
	    txo = _newTexture(texture);
	    if (! txo)
		goto error;

	    _dxf_InsertObject(TEXTUREHASH, texture, txo);
	}

	xf->texture_field = DXReference(txo);
	txp = (Texture)DXGetPrivateData((Private)txo);

	xf->textureWidth  = txp->width;
	xf->textureHeight = txp->height;
	xf->texture       = txp->pixels;
    }
    else
    {
	xf->texture_field = NULL;
	xf->uv_array      = NULL;
	xf->texture       = NULL;
    }

    return OK;

error:
    if (xf->texture_field)
	DXDelete((dxObject)xf->texture_field);
    if (xf->uv_array)
	DXDelete((dxObject)xf->uv_array);

    xf->texture_field = NULL;
    xf->texture = NULL;
    xf->uv_array = NULL;
    xf->uv = NULL;
    xf->myTextureData = 0;

    return ERROR;
}

static Error
_getTexture_Field(Texture t)
{
    Type  type;
    Category cat;
    int   n, rank, shape[32], counts[2];
    Array tmp;
    int i, sizeError = 0, s0, s1;
    dxObject attr;
    Field tf = (Field)t->textureObj;

    tmp = (Array)DXGetComponentValue(tf, "connections");
    if (! tmp)
    {
	DXSetError(ERROR_INVALID_DATA, 
	    "texture field has no connections");
	goto error;
    }

    if (! DXQueryGridConnections(tmp, &n, counts))
    {
	DXSetError(ERROR_INVALID_DATA, 
	    "texture field must have regular connections");
	goto error;
    }

    if (n != 2)
    {
	DXSetError(ERROR_INVALID_DATA, 
	    "texture field must be 2-d regular connections");
	goto error;
    }

    t->width  = counts[1];
    t->height = counts[0];

    if (counts[0] < 64 || counts[1] < 64)
    {
	DXSetError(ERROR_INVALID_DATA,
	    "texture map resolution must be at least 64x64");
	goto error;
    }
	    
    for (s0 = s1 = 0, i = 1; i && s0 < 2 && s1 < 2; i <<= 1)
    {
	s0 += (counts[0] & i) ? 1 : 0;
	s1 += (counts[1] & i) ? 1 : 0;
    }

    if (i)
    {
	DXSetError(ERROR_INVALID_DATA,
	    "texture map resolution must be powers of 2");
	goto error;
    }

    tmp = (Array)DXGetComponentValue(tf, "colors");
    if (! tmp)
	tmp = (Array)DXGetComponentValue(tf, "data");
    if (! tmp)
    {
	DXSetError(ERROR_INVALID_DATA, 
	    "texture has neither colors nor data");
	return ERROR;
    }

    DXGetArrayInfo(tmp, &n, &type, &cat, &rank, shape);

    if (type == TYPE_UBYTE && rank == 1 && shape[0] == 3)
    {
	t->pixels = (ubyte *)DXGetArrayData(tmp);
	t->myPixels = 0;
    }
    else
    {
	t->pixels = (ubyte *)tdmAllocate(3*n*sizeof(ubyte));
	if (! t->pixels)
	    goto error;
	t->myPixels = 1;
    
	if (type == TYPE_FLOAT && rank == 1 && shape[0] == 3)
	{
	    int i;
	    ubyte *dst = t->pixels;
	    float *src = (float *)DXGetArrayData(tmp);
	    for (i = 0; i < 3*n; i++)
	    {
		float c = *src++;
		if (c < 0)        *dst++ = 0;
		else if (c > 1.0) *dst++ = 255;
		else              *dst++ = (ubyte)(c * 255);
	    }
	}
	else if (type == TYPE_UBYTE &&
	    (rank == 0 || rank == 1 && shape[0] == 1))
	{
	    int i, m;
	    ubyte *dst = t->pixels;
	    ubyte *src = (ubyte *)DXGetArrayData(tmp);
	    Array cmap_array;
	    ubyte map_array[768];
	    ubyte *map;

	    cmap_array = (Array)DXGetComponentValue(tf, "color map");
	    if (! cmap_array)
	    {   
		DXSetError(ERROR_MISSING_DATA, 
		    "texture map contains scalar bytes but no color map");
		    goto error;
	    }

	    DXGetArrayInfo(cmap_array, &m, &type, NULL, &rank, shape);

	    if (type == TYPE_FLOAT && rank == 1 && shape[0] == 3)
	    {
		float *map_src = (float *)DXGetArrayData(cmap_array);

		for (i = 0; i < 3*m; i++)
		{
		    float c = *map_src++;
		    if      (c < 0.0) map_array[i] = 0;
		    else if (c > 1.0) map_array[i] = 255;
		    else 	      map_array[i] = (ubyte)(c * 255);
		}
		map = map_array;
	    }
	    else if (type == TYPE_UBYTE && rank == 1 && shape[0] == 3)
	    {
		map = (ubyte *)DXGetArrayData(cmap_array);
	    }
	    else
	    {
		DXSetError(ERROR_INVALID_DATA,
		    "invalid colormap type in texture");
		goto error;
	    }

	    for (i = 0; i < n; i++)
	    {
		ubyte *c = map + 3 * *src++;
		*dst++ = *c++;
		*dst++ = *c++;
		*dst++ = *c++;
	    }
	}
	else
	{ 
	    DXSetError(ERROR_INVALID_DATA,
		"texture must be float or ubyte 3-vectors or %s",
		"contain scalar ubytes and have a color map");
	    goto error;
	}
    }
	
    return OK;

error:
    return ERROR;
}

static Error
_getTexture_CompositeField(Texture t)
{
    Type  type;
    Category cat;
    int   n, rank, shape[32], counts[2];
    int i, sizeError = 0, s0, s1;
    dxObject attr;
    Group cf = (Group)t->textureObj;
    int nMembers;

    DXGetMemberCount(cf, &nMembers);

    counts[0] = counts[1] = 0;

    for (i = 0; i < nMembers; i++)
    {
	Field member;
	Array grid;
	int memberCounts[2], memberOffsets[2], nDim, cnt;

	member = (Field)DXGetEnumeratedMember(cf, i, NULL);
	grid = (Array)DXGetComponentValue(member, "connections");

	if (!DXQueryGridConnections(grid, &nDim, memberCounts) ||
	    !DXGetMeshOffsets((MeshArray)grid, memberOffsets))
	{
	    DXSetError(ERROR_INVALID_DATA, 
		"texture field must have regular connections");
	    goto error;
	}

	if (nDim != 2)
	{
	    DXSetError(ERROR_INVALID_DATA, "texture fields must be 2-D");
	    goto error;
	}

	if ((cnt = memberOffsets[0] + memberCounts[0]) > counts[0])
	    counts[0] = cnt;

	if ((cnt = memberOffsets[1] + memberCounts[1]) > counts[1])
	    counts[1] = cnt;
    }

    t->width  = counts[1];
    t->height = counts[0];

    if (counts[0] < 64 || counts[1] < 64)
    {
	DXSetError(ERROR_INVALID_DATA,
	    "texture map resolution must be at least 64x64");
	goto error;
    }
	    
    for (s0 = s1 = 0, i = 1; i && s0 < 2 && s1 < 2; i <<= 1)
    {
	s0 += (counts[0] & i) ? 1 : 0;
	s1 += (counts[1] & i) ? 1 : 0;
    }

    if (i)
    {
	DXSetError(ERROR_INVALID_DATA,
	    "texture map resolution must be powers of 2");
	goto error;
    }

    n = counts[0] * counts[1];

    t->pixels = (ubyte *)tdmAllocate(3*n*sizeof(ubyte));
    if (! t->pixels)
	goto error;
    t->myPixels = 1;

    for (i = 0; i < nMembers; i++)
    {
	Field member;
	Array texture;
	Array grid;
	int memberCounts[2], memberOffsets[2], nDim, cnt;

	member = (Field)DXGetEnumeratedMember(cf, i, NULL);
	grid = (Array)DXGetComponentValue(member, "connections");

	texture = (Array)DXGetComponentValue(member, "colors");
	if (! texture)
	    texture = (Array)DXGetComponentValue(member, "data");
	if (! texture)
	{
	    DXSetError(ERROR_INVALID_DATA, 
		"texture has neither colors nor data");
	    return ERROR;
	}

	DXQueryGridConnections(grid, NULL, memberCounts);
	DXGetMeshOffsets((MeshArray)grid, memberOffsets);

	DXGetArrayInfo(texture, NULL, &type, &cat, &rank, shape);

	if (type == TYPE_UBYTE && rank == 1 && shape[0] == 3)
	{
	    int j, k;
	    ubyte *src = (ubyte *)DXGetArrayData(texture);
	    ubyte *dst = t->pixels + 3*((memberOffsets[0]*counts[1])
					     + memberOffsets[1]);
	    int src_stride = 3*counts[1];
	    int dst_stride = 3*memberCounts[1];

	    int cpy_length = src_stride;

	    for (j = 0; j < memberCounts[0]; j++)
	    {
		memcpy(dst, src, cpy_length);
		dst += dst_stride;
		src += src_stride;
	    }
	}
	else if (type == TYPE_FLOAT && rank == 1 && shape[0] == 3)
	{
	    int j, k;
	    float *src = (float *)DXGetArrayData(texture);
	    ubyte *dst = t->pixels + 3*((memberOffsets[0]*counts[1])
					     + memberOffsets[1]);
	    int dst_stride = 3*(counts[1] - memberCounts[1]);

	    for (j = 0; j < memberCounts[0]; j++)
	    {
		for (k = 0; k < memberCounts[1]; k++)
		{
		    float c = *src++;
		    if (c < 0)        *dst++ = 0;
		    else if (c > 1.0) *dst++ = 255;
		    else              *dst++ = (ubyte)(c * 255);
		}
		dst += dst_stride;
	    }
	}
	else if (type == TYPE_UBYTE &&
	    (rank == 0 || rank == 1 && shape[0] == 1))
	{
	    int j, k, m;
	    Array cmap_array;
	    ubyte map_array[768];
	    ubyte *map;
	    ubyte *src = (ubyte *)DXGetArrayData(texture);
	    int dst_stride = 3*(counts[1] - memberCounts[1]);
	    ubyte *dst = t->pixels + 3*((memberOffsets[0]*counts[1])
					     + memberOffsets[1]);

	    cmap_array = (Array)DXGetComponentValue(member, "color map");
	    if (! cmap_array)
	    {   
		DXSetError(ERROR_MISSING_DATA, 
		    "texture map contains scalar bytes but no color map");
		    goto error;
	    }

	    DXGetArrayInfo(cmap_array, &m, &type, NULL, &rank, shape);

	    if (type == TYPE_FLOAT && rank == 1 && shape[0] == 3)
	    {
		float *map_src = (float *)DXGetArrayData(cmap_array);

		for (j = 0; j < 3*m; j++)
		{
		    float c = *map_src++;
		    if      (c < 0.0) map_array[j] = 0;
		    else if (c > 1.0) map_array[j] = 255;
		    else 	      map_array[j] = (ubyte)(c * 255);
		}
		map = map_array;
	    }
	    else if (type == TYPE_UBYTE && rank == 1 && shape[0] == 3)
	    {
		map = (ubyte *)DXGetArrayData(cmap_array);
	    }
	    else
	    {
		DXSetError(ERROR_INVALID_DATA,
		    "invalid colormap type in texture");
		goto error;
	    }

	    for (j = 0; j < memberCounts[0]; j++)
	    {
		for (k = 0; k < memberCounts[1]; k++)
		{
		    ubyte *c = map + (3 * *src);
		    *dst++ = *c++;
		    *dst++ = *c++;
		    *dst++ = *c++;
		    src ++;
		}
		dst += dst_stride;
	    }
	}
	else
	{ 
	    DXSetError(ERROR_INVALID_DATA,
		"texture must be float or ubyte 3-vectors or %s",
		"contain scalar ubytes and have a color map");
	    goto error;
	}
    }
	
    return OK;

error:
    return ERROR;
}
