/***********************************************************************/
/* 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 <stdio.h>
#include <math.h>
#include <string.h>
#include <dx/dx.h>
#include "interpClass.h"

typedef long CRC;

Interpolator _dxfNewGroupInterpolator(Group, 
			enum interp_init, float, Matrix *);

Interpolator _dxfSelectFieldInterpolator(Field, 
			enum interp_init, float, Matrix *);

static Error _dxfValidInterpolatorMap(Object);


static struct PrimTypes {
    char 	*name;
    int		dimensionality;
    int		ptsPerPrimitive;
} _dxdprimTypes[] = 
{
    { "lines",		1,	2 },
    { "triangles",	2,	3 },
    { "quads",		2,	4 },
    { "cubes",		3,	8 },
    { "tetrahedra",	3,	4 },
    { NULL,		0,	0 }
};

Interpolator
DXNewInterpolator(Object o, enum interp_init initType, float fuzz)
{
    Interpolator interp = NULL;
    Object copy = NULL;

    if (fuzz < 0.0)
	fuzz = FUZZ;

    if (! _dxfValidInterpolatorMap(o))
	goto error;

    copy = DXCopy(o, COPY_HEADER);
    if (! copy)
	goto error;

    if (initType == INTERP_INIT_PARALLEL)
	DXCreateTaskGroup();
    
    interp = _dxfNewInterpolatorSwitch(copy, initType, fuzz, NULL);
    if (! interp)
    {
	if (initType == INTERP_INIT_PARALLEL)
	    DXAbortTaskGroup();

	goto error;
    }

    if (initType == INTERP_INIT_PARALLEL)
	if (! DXExecuteTaskGroup() || DXGetError() != ERROR_NONE)
	    goto error;

    interp->fuzz = fuzz;

    interp->rootObject = DXReference(copy);

    return interp;

error:
    DXDelete((Object)interp);
    DXDelete((Object)copy);
    return NULL;
}

Interpolator
_dxfNewInterpolatorSwitch(Object o, 
		enum interp_init initType, float fuzz, Matrix *stack)
{
    Interpolator interp = NULL;
    Class        class;
    Object       child;
    Matrix	 mat, prod;

    class = DXGetObjectClass(o);
    /*
    if (class == CLASS_GROUP)
	class = DXGetGroupClass((Group)o);
    */
    
    /*
     * Determine class of input and call appropriate Interpolator
     * sub-class method
     */
    switch (class)
    {
	case CLASS_XFORM:
	    if (stack)
	    {
		if (! DXGetXformInfo((Xform)o, &child, &mat))
		    goto error;
	    
		prod = DXConcatenate(mat, *stack);
	    }
	    else
		if (! DXGetXformInfo((Xform)o, &child, &prod))
		    goto error;

	    interp = _dxfNewInterpolatorSwitch(child,
						initType, fuzz, &prod);

	    if (! interp)
		goto error;
	    
	    break;
	    
	case CLASS_GROUP:
	    interp = (Interpolator)_dxfNewGroupInterpolator((Group)o,
						    initType, fuzz, stack);
	    if (! interp)
		goto error;

	    break;
	case CLASS_FIELD:
	    interp = (Interpolator)_dxfSelectFieldInterpolator((Field)o,
						    initType, fuzz, stack);
	    if (! interp)
		goto error;

	    break;
	default:
	    DXSetError(ERROR_BAD_CLASS, "#11381", "map");
	    interp = NULL;
    }

    return interp;

error:
    DXDelete((Object)interp);
    return NULL;
}

Interpolator
_dxf_NewInterpolator(struct interpolator_class *class, Object o)
{
    Interpolator i;

   i =  (Interpolator)_dxf_NewObject((struct object_class *)class);
   if (! i)
       return NULL;

    i->dataObject = o;
    i->rootObject = NULL;

    memset(&i->matrix, 0, sizeof(Matrix));

    return i;
}

int
_dxfInterpolator_Delete(Interpolator interp)
{
    if (interp->rootObject)
	DXDelete(interp->rootObject);
    return OK;
}

Interpolator
DXInterpolate(Interpolator interp, int *n, float *p, Pointer v)
{
    int nInterpolated, nAtStart;

    /*
     * Try to interpolate a list of points.  First, try interpolation
     * without fuzz; if points remain, we'll have to try interpolation
     * with fuzz.
     */
    if (! _dxfInterpolate(interp, n, &p, &v, NULL, FUZZ_OFF))
	return NULL;
    
    /*
     * If points remain, loop.  When the fuzz flag is turned on, the
     * first point will be interpolated with fuzz.  If this succeeds,
     * subsequent interpolations will proceed without fuzz until a point
     * cannot be interpolated.  At this point, control passes back up here
     * and we start again with fuzz.  This loop terminates when no point is
     * interpolated with or without fuzz or when all points have been 
     * interpolated.
    */
    if (interp->fuzz > 0.0 && *n)
	do
	{
	    nAtStart = *n;

	    if (! _dxfInterpolate(interp, n, &p, &v, NULL, FUZZ_ON))
		return NULL;
	
	    nInterpolated = nAtStart - *n;

	} while (nInterpolated && *n);

    return interp;
}

Object
_dxfInterpolator_GetType(Interpolator interp, Type *type, 
			Category *category, int *rank, int *shape)
{
    if (interp->rootObject == NULL)
	return NULL;
    else
	return DXGetType(interp->rootObject, type, category, rank, shape);
}

Interpolator
_dxf_CopyInterpolator(Interpolator new, Interpolator src)
{
    int 	 i;

    if (src->rootObject)
	new->rootObject = DXReference(src->rootObject);
    else
	new->rootObject = NULL;

    new->dataObject = src->dataObject;

    new->fuzz = src->fuzz;

    new->nDim = src->nDim;
    for (i = 0; i < src->nDim; i++)
    {
	new->max[i] = src->max[i];
	new->min[i] = src->min[i];
    }

    new->type     = src->type;
    new->category = src->category;
    new->rank     = src->rank;
    new->matrix   = src->matrix;

    for (i = 0; i < src->rank; i++)
	 new->shape[i] = src->shape[i];
	
    return new;
}

static Error
_dxfValidInterpolatorMap(Object map)
{
    char *name;
    Class class;

    name = NULL;

    class = DXGetObjectClass(map);
    if (class == CLASS_GROUP)
	class = DXGetGroupClass((Group)map);

    if (class == CLASS_COMPOSITEFIELD || class == CLASS_MULTIGRID)
    {
	Object  child;
	int	i;

	i = 0;
	while (NULL != (child = DXGetEnumeratedMember((Group)map, i++, NULL)))
	    if (! _dxfValidInterpolatorMap(child))
		return ERROR;
	
	return OK;
    }
    else if (class == CLASS_XFORM)
    {
	Object c;

	if (! DXGetXformInfo((Xform)map, &c, NULL))
	    return ERROR;
	
	return _dxfValidInterpolatorMap(c);
    }
    else if (class == CLASS_FIELD)
    {
	struct PrimTypes *prim;
	Array  		 array;
	int   		 rank, shape[32];
	int		 nPrims;
	Object		 at;

	/*
	 * Allow empty fields and fields with no connections
	 */
	if (DXEmptyField((Field)map))
	    return OK;
	
	array = (Array)DXGetComponentValue((Field)map, "connections");
	if (! array)
	    return OK;
	
	DXGetArrayInfo(array, &nPrims, NULL, NULL, NULL, NULL);
	if (nPrims < 0)
	    return OK;

	at = DXGetComponentAttribute((Field)map, "connections", "element type");
	if (! at || DXGetObjectClass(at) != CLASS_STRING)
	{
	    DXSetError(ERROR_MISSING_DATA, "#10255", "connections", 
							"element type");
	    return ERROR;
	}

	name = DXGetString((String)at);

	for (prim = _dxdprimTypes; prim->name != NULL; prim ++)
	    if (! strcmp(prim->name, name))
		break;

	if (prim->name == NULL)
	{
	    DXSetError(ERROR_INVALID_DATA, "#11380", "name");
	    return ERROR;
	}

	array = (Array)DXGetComponentValue((Field)map, "positions");
	if (! array)
	{
	    DXSetError(ERROR_MISSING_DATA, "#10240", "map", "positions");
	    return ERROR;
	}

	DXGetArrayInfo(array, NULL, NULL, NULL, NULL, shape);

	if (shape[0] != prim->dimensionality)
	{
	    DXSetError(ERROR_INVALID_DATA, "#11003",
				    prim->name, prim->dimensionality);
	    return ERROR;
	}

	array = (Array)DXGetComponentValue((Field)map, "connections");
	DXGetArrayInfo(array, NULL, NULL, NULL, &rank, shape);

	if (rank != 1 || shape[0] != prim->ptsPerPrimitive)
	{
	    DXSetError(ERROR_INVALID_DATA, "#11004",
			prim->name, prim->ptsPerPrimitive);
	    return ERROR;
	}

	return OK;
    }
    else
    {
	DXSetError(ERROR_INVALID_DATA, "#11381");
	return ERROR;
    }
}

Interpolator
DXLocalizeInterpolator(Interpolator o)
{
    return _dxfLocalizeInterpolator(o);
}
