/***********************************************************************

     FONCTION :
     ----------
        File OpenGl_togl_texture.c :
 

     REMARQUES:
     ---------- 
      

     HISTORIQUE DES MODIFICATIONS   :
     --------------------------------
       05-08-97 : PCT ; Support texture mapping
			ajout pour deuxieme passe du texture mapping

************************************************************************/

#define G003	/* EUG 16-09-99 ZBufferAuto treatment
*/

/*----------------------------------------------------------------------*/
/*
 * Includes
 */

#include <OpenGl_tgl_all.h>

#include <stddef.h>
#include <stdio.h>

#include <OpenGl_cmn_memory.h>
#include <OpenGl_cmn_stg_tbl.h>
#include <OpenGl_cmn_htbl.h>
#include <OpenGl_cmn_varargs.h>
#include <OpenGl_tsm.h>
#include <OpenGl_tsm_ws.h>

static  MtblPtr   TsmMtblArray[TelLast];

/* Fixed Header info for a structure */
typedef  struct  TSM_HEAD_STRUCT
{
    Tint    num;               /* number of elements present */
} TSM_HEAD, *tsm_head;

/* A Structure having fixed header and array of elements */
typedef  struct  TSM_STRUCT_STRUCT
{
    TSM_HEAD  header;
    Tint      alloc;           /* number of elements that can be accomodated */
    TSM_ELEM  elem[1];
} TSM_STRUCT, *tsm_struct;

/* A node containing an elem when structure is in the form of a list */
typedef  struct  TSM_NODE_STRUCT
{
    struct  TSM_NODE_STRUCT  *next;
    struct  TSM_NODE_STRUCT  *prev;
    TSM_ELEM   elem;
} TSM_NODE, *tsm_node;

/* The head of the list when the structure is in the form of a list */
typedef  struct  TSM_LHEAD_STRUCT
{
    tsm_node  next;
    tsm_node  prev;
    TSM_HEAD  header;
} TSM_LHEAD, *tsm_lhead;

#define    TsmStructureNotOpen      -1
#define    TsmInvalidStructure      -1

/* Context maintained for the currently open structure */
typedef  struct  TSM_CONTEXT_STRUCT
{
    Tint     stid;   /* Current  Open Structure */
    Tint      ind;   /* Current  Element Index  */
		     /* = TsmStructureNotOpen if no structure currently open */
    tsm_struct  s;   /* Current  Structure Pointer */
    TSM_LHEAD   l;   /* Current  Structure List Head */
    tsm_node    n;   /* Current  Element list node */
} TSM_CONTEXT, *tsm_context;

static  TSM_CONTEXT   context = { TsmInvalidStructure, TsmStructureNotOpen };

static  TEditMode     edit_mode;

#define  TSM_HTBL_SIZE  251

static  cmn_stg_tbl   node_tbl;  /* Table for list nodes containing elements */
static  cmn_htbl      stid_tbl;  /* Hash table for Stid Vs Structure ptr */

                      /* Global workstation variable */
Tint    TglActiveWs;		    /* valid only during traversals */

void
TsmInitAllClasses( MtblPtr  (**tbl)(TelType*), Tint size )
{
    register  Tint  i;
    TelType   el;
    register  MtblPtr   fp;

    for( i=0; i<size; i++ )
    {
        fp = tbl[i](&el);
        TsmMtblArray[el] = fp;
    }
    return;
}

/* This function invokes the required method of the element type by looking
   up an array of Method Tables of each element.
   The arguments passed to the method are the data of the element and any
   other data that needs to be sent.
   In the case of ADD_TO_STRUCTURE methods, data should be the address of the
   element data. Other wise, it is the element data itself
*/

TStatus
TsmSendMessage( TelType el, TMsgType msg, TSM_ELEM_DATA data, Tint n, ... )
{
    TStatus  (*fptr)( TSM_ELEM_DATA, Tint, cmn_key* );
#if defined (SOLARIS) || defined (IRIXO32)
    cmn_key  *k;
#else
    cmn_key  k[TMaxArgs];
#endif
    TStatus  TheStatus = TSuccess;

    CMN_GET_STACK( n, k );
    fptr = TsmMtblArray[el][msg];
    if( fptr )
        TheStatus = fptr( data, n, k );
    return  TheStatus;
}


static  TStatus
TsmArrayToList( tsm_struct s )
{
    Tint       i;
    tsm_node   node, prev;


    /* for each element in array
	  allocate node
	  patch pointers
	  set node type and data */
   prev = (tsm_node)&context.l;
   for( i=0; i<s->header.num; i++ )
   {
       node = cmn_stg_tbl_get( node_tbl );
       if( !node )
	   return TFailure;
       node->elem = s->elem[i];
       node->next = prev->next;
       node->prev = prev;
       prev->next = node;
       node->next->prev = node;
       prev = node;
   }

   /* free structure memory */
       /* deferred till close structure */

   return TSuccess;
}


static  tsm_struct
TsmListToArray( void )
{
    Tint      i, num;
    tsm_node  node;
    tsm_struct s;
    tsm_lhead  l;

    /* allocate structure memory using number of elements */
    s = context.s, l = &context.l;
    if( !s || l->header.num > s->alloc )
    {
	num = l->header.num;
	if( !num )
	    return 0; /* This is a valid return value and not an error */

	if( s )
	    cmn_freemem( s );

	s = context.s = cmn_getmem( 1,   sizeof(TSM_ELEM) * num
				       + offsetof( TSM_STRUCT, elem ), 1 );
	if( !s )
	    return 0;

	/* modify hash table data entry for stid */
	cmn_add_in_htbl( stid_tbl, context.stid, s );

	s->alloc = num;
    }
    s->header = l->header;

    /* set structure header and element type and data by traversing the list */
    for( i=0, node=l->next, num=s->header.num;
		      i<num; i++, node = node->next )
    {
	s->elem[i] = node->elem;
    }

    /* free list nodes and then list head */
       /* deferred till next open structure call */

    /* return structure pointer */
    return s;
}

static  void
TsmFreeList( Tint sm_flag )
{
    tsm_node  node, next;
    Tint      num, i;

    num = context.l.header.num,
    next = context.l.next;

    for( i=0; i<num; i++ )
    {
	node = next;
	if( sm_flag )
	    TsmSendMessage( node->elem.el, Delete, node->elem.data, 0 );
	next = node->next;
	cmn_stg_tbl_free( node );
    }
    context.l.next = context.l.prev = context.n = (tsm_node)&context.l;
    return;
}

TStatus
TsmOpenStructure( Tint  stid )
{
    cmn_htbl_elem   rec;
    tsm_struct      s;

    if( context.ind != TsmStructureNotOpen )
	return TFailure; /* Some structure already open */

    if( !stid_tbl )
    {
	stid_tbl = cmn_create_htbl( TSM_HTBL_SIZE );
	if( !stid_tbl )
	    return TFailure;
    }

    if( !node_tbl )
    {
	node_tbl = cmn_stg_tbl_create( 30, sizeof(TSM_NODE) );
	if( !node_tbl )
	    return TFailure;
    }

    if( stid != context.stid )
	TsmFreeList( 0 );

    /* Look in hash table for stid */
    rec = cmn_find_in_htbl( stid_tbl, stid, (void **)&s );
    if( !rec )
    {
	cmn_add_in_htbl( stid_tbl, stid, 0 );
	context.stid = stid,
	context.ind = 0,
	context.s = 0,
	context.l.header.num = 0;
    }
    else
    {
	if( stid != context.stid )
	{
	    context.stid = stid,
	    context.s = s;
	 /* s might be null if structure is created and closed with num = 0 */
	 /* and then reopened */
	    if( s )
	    {
	        TsmArrayToList( s );
	        context.ind = s->header.num,
	        context.l.header = s->header;
	        context.n = context.l.prev;
	    }
	    else
	    {
		context.ind = 0;
	    }
	}
	else /* reuse previous list */
	{
	    context.ind = context.l.header.num,
	    context.n = context.l.prev,
	    context.s = s;
	}
    }

    /* Ajout CAL, 14/09/95 */
    /* on force l'update_state de toutes les wks a TNotDone */
    TsmInitUpdateState ();

    return TSuccess;
}

TStatus
TsmCloseStructure( void )
{
    if( context.ind == TsmStructureNotOpen )
	return TFailure; /* No structure currently open */

    /* Convert list to array */
    TsmListToArray();

    context.ind = TsmStructureNotOpen;

    return TSuccess;
}


TStatus
TsmDisplayStructure( Tint stid, Tint  wsid )
{
    /* Tint           i, num; */
    /* cmn_htbl_elem  rec; */
    /* tsm_struct     s; */
    TSM_ELEM_DATA  data;
    CMN_KEY        key;

    TglActiveWs = wsid;

    if( context.ind != TsmStructureNotOpen )
	return TFailure; /* Some structure currently open */

    key.id = wsid;
    data.ldata = stid;
    TsmSendMessage( TelExecuteStructure, DisplayTraverse, data, 1, &key );

    return TSuccess;
}

TStatus
TsmPrintStructure( Tint stid )
{
    Tint           i, num;
    cmn_htbl_elem  rec;
    tsm_struct     s;

    if( context.ind != TsmStructureNotOpen )
	return TFailure; /* Some structure currently open */

    if( !stid_tbl )
	return TFailure; /* No structure created yet */

    /* Obtain structure pointer from hash table */
    rec = cmn_find_in_htbl( stid_tbl, stid, (void **)&s );
    if( !rec )
	return TFailure; /* Non-existent structure */

    printf( "\nPRINT:" );
    printf( "\n\tSTRUCTURE ID = %d", stid );
    if( !s )
    {
	num = 0;
    }
    else
    {
        num = s->header.num;
    }

    printf( "\n\tNUMBER OF ELEMENTS = %d", num);
    printf( "\n" );

    /* For each element Send Message Print */
    for( i = 0; i < num; i++ )
    {
	printf("\n\tElement[%d] : ", i+1);
	TsmSendMessage( s->elem[i].el, Print, s->elem[i].data, 0 );
    }

    return TSuccess;
}

TStatus
TsmAddToStructure( TelType  el, Tint n, ... )
{
    TSM_ELEM_DATA    data;
#if defined (SOLARIS) || defined (IRIXO32)
    cmn_key  *k;
#else
    cmn_key  k[TMaxArgs];
#endif

    if( context.ind == TsmStructureNotOpen )
	return TFailure; /* No structure currently open */

    CMN_GET_STACK( n, k );
    data.pdata = &data;
    if( TsmSendMessage( el, Add, data, -n, k ) == TFailure )
	return TFailure;

    if( edit_mode == TEditReplace )
    {
       TsmSendMessage( context.n->elem.el, Delete, context.n->elem.data, 0 );
    }
    else
    {
       tsm_node node;

       node = cmn_stg_tbl_get( node_tbl );
       if( !node )
           return TFailure;

       /* insert the node in the linked list after the current node */
       node->prev       = context.n;
       node->next       = context.n->next;
       context.n->next  = node;
       node->next->prev = node;

       /* Make this node as current node */
       context.n = node;
       context.ind++;

       /* Increment the no of elements */
       context.l.header.num++;
    }

    context.n->elem.el = el;
    context.n->elem.data = data;

    return TSuccess;
}

TStatus
TsmEmptyStructure( Tint stid )
{
    cmn_htbl_elem rec;
    tsm_struct    s;

    if( !stid_tbl )
	return TFailure; /* No structure created yet */

    /* Look in hash table for stid */
    rec = cmn_find_in_htbl( stid_tbl, stid, (void **)&s );
    if( !rec )
	return TFailure; /* Non-existent structure */

    /* For each element send message DELETE element */
    if( context.ind != TsmStructureNotOpen && context.stid == stid )
    {
	TsmFreeList( 1 );
	context.l.header.num = 0;
	context.ind = 0;
    }
    else if( s )
        s->header.num = 0;

    return TSuccess;
}


TStatus
TsmDeleteStructure( Tint stid )
{
    Tint  i, num;
    cmn_htbl_elem  rec;
    tsm_struct     s;

    if( context.ind != TsmStructureNotOpen && context.stid == stid )
	return TFailure; /* Same structure currently open */

    if( !stid_tbl )
	return TFailure; /* No structure created yet */

    rec = cmn_find_in_htbl( stid_tbl, stid, (void **)&s );
    if( !rec )
	return TFailure; /* Non-existent structure */

    if( stid == context.stid )
	/* Ensure that the list is freed on subsequent OpenStruct call */
	/* even when same structure id is reused */
        context.stid = TsmInvalidStructure;

    if( s )
    {
	num = s->header.num;
        /* For each element send message DELETE element */
        for( i=0; i<num; i++ )
        {
	    TsmSendMessage( s->elem[i].el, Delete, s->elem[i].data, 0 );
        }
        /* Free structure memory */
	cmn_freemem( s );
    }

    /* Remove hash table entry */
    cmn_delete_from_htbl( stid_tbl, stid, (void **)&s );

    /* Ajout BGN, 27/05/97 */
    /* on force l'update_state de toutes les wks a TNotDone */
    TsmInitUpdateState ();

    return TSuccess;
}

TEditMode
TsmSetEditMode( TEditMode mode )
{
   TEditMode m;

   m = edit_mode;

   /* Set static variable for mode */
   edit_mode = mode;

   /* return previous mode */
   return m;
}

TStatus
TsmDeleteElement( void )
{
   tsm_node node;

   if( context.ind == TsmStructureNotOpen )
       return TFailure; /* No structure currently open */

   if( !context.ind )
       return TFailure;

   /* Send message DELETE element */
   TsmSendMessage( context.n->elem.el, Delete, context.n->elem.data, 0 );

   node = context.n;
   node->prev->next = node->next;
   node->next->prev = node->prev;

   /* make previous node as current node */
   context.n = node->prev;
   context.ind--;

   /* Decrement the no of elements */
   context.l.header.num--;

   /* free the linked list node */
   cmn_stg_tbl_free( node );

   return TSuccess;
}

TStatus
TsmDeleteElementsBetweenLabels( Tint label_id1, Tint label_id2 )
                                           /* label_id1 & label_id2 exclusive */
{
   Tint elem1, elem2;

   if( context.ind == TsmStructureNotOpen )
       return TFailure; /* No structure currently open */

   TsmSetElementPointer( 0 );

   if( TsmSetElementPointerAtLabel( label_id1 ) == TFailure)
       return TFailure;
   elem1 = context.ind;

   if( TsmSetElementPointerAtLabel( label_id2 ) == TFailure)
       return TFailure;
   elem2 = context.ind;

   TsmDeleteElementRange( elem1, elem2 );

   return TSuccess;
}

TStatus
TsmDeleteElementRange( Tint elem1, Tint elem2 ) /* elem1 & elem2 exclusive */
{
   if( context.ind == TsmStructureNotOpen )
       return TFailure; /* No structure currently open */

   TsmSetElementPointer( elem2 - 1 );
   while( context.ind != elem1 && context.ind )
       TsmDeleteElement();

   return TSuccess;
}

TStatus
TsmSetElementPointer( Tint index )
{
    if( context.ind == TsmStructureNotOpen )
        return TFailure; /* No structure currently open */

    /* Set static variable accordingly */
    if( context.ind == index )
    {
	return TSuccess;
    }
    if( index > context.l.header.num )
    {
	index = context.l.header.num;
	context.n = context.l.prev;
    }
    else if( !index )
    {
	context.n = (tsm_node)&context.l;
    }
    else if( index < 0 )
    {
	index = 0;
	context.n = (tsm_node)&context.l;
    }
    else
    {
	Tint     a, b, c, d;
	tsm_node node;

	a = index - 0;
	b = context.ind - index; if( b < 0 ) b = -b; /* b = labs(b) */
	c = context.l.header.num - index;
        d = ( a < b ) ? ( a < c ? a : c ) : ( b < c ? b : c ) ;

        if( a == d )
	{
	    /* Traverse from head in forward direction */
	    d = index-1;
            node = context.l.next;
	    while( d-- )
	    {
                node = node->next;
	    }
	}
	else if( b == d )
	{
	    /* Traverse from current node in appropriate direction */
	    node = context.n;
	    d    = context.ind;
	    if( context.ind < index )
	    {
	       while( d != index )
	       {
		  node = node->next;
		  d++;
	       }
	    }
	    else
	    {
	       while( d != index )
	       {
		  node = node->prev;
		  d--;
	       }
	    }
	}
	else /* ( c == d ) */
	{
	    /* Traverse from head in backward direction */
	    d = context.l.header.num;
            node = context.l.prev;
	    while( d != index )
	    {
                node = node->prev;
		d--;
	    }
	}

	context.n = node;
    }

    context.ind = index;

    return TSuccess;
}


TStatus
TsmSetElementPointerAtLabel( Tint label_id )
{
    Tint   i, num;
    tsm_node  node;

    if( context.ind == TsmStructureNotOpen )
        return TFailure; /* No structure currently open */

    num = context.l.header.num;
    for( i=context.ind+1, node=context.n->next; i<=num; i++, node=node->next )
    {
	if( node->elem.el == TelLabel && node->elem.data.ldata == label_id )
	{
	    context.ind = i;
	    context.n = node;
	    return TSuccess;
	}
    }

    return TFailure;
}


TStatus
TsmSetElementPointerAtPickId( Tint pick_id )
{
    Tint   i, num;
    tsm_node  node;

    if( context.ind == TsmStructureNotOpen )
        return TFailure; /* No structure currently open */

    num = context.l.header.num;
    for( i=context.ind+1, node=context.n->next; i<=num; i++, node=node->next )
    {
	if( node->elem.el == TelPickId && node->elem.data.ldata == pick_id )
	{
	    context.ind = i;
	    context.n = node;
	    break;
	}
    }

    return TSuccess;
}

TStatus
TsmOffsetElementPointer( Tint offset )
{
    return TsmSetElementPointer( context.ind + offset );
}

TStatus
TsmPickStructure( Tint  stid )
{
    Tint           i, num;
    cmn_htbl_elem  rec;
    tsm_struct     s;

    if( context.ind != TsmStructureNotOpen )
	return TFailure; /* Some structure currently open */

    if( !stid_tbl )
	return TFailure; /* No structure created yet */

    /* Obtain structure pointer from hash table */
    rec = cmn_find_in_htbl( stid_tbl, stid, (void**)&s );
    if( !rec )
	return TFailure; /* Non-existent structure */

    /* For each element Send Message PickTraverse */
    num = s->header.num;
    for( i = 0; i < num; i++ )
    {
	TsmSendMessage( s->elem[i].el, PickTraverse, s->elem[i].data, 0 );
    }

    return TSuccess;
}


TStatus
TsmGetStructure( Tint stid, Tint *num, tsm_elem *e )
{
    cmn_htbl_elem  rec;
    tsm_struct     s;

    if( !stid_tbl )
	return TFailure; /* No structure created yet */

    /* Obtain structure pointer from hash table */
    rec = cmn_find_in_htbl( stid_tbl, stid, (void **)&s );
    if( !rec )
	return TFailure; /* Non-existent structure */

    *num = 0;

    if( s )
       *num = s->header.num;

    *e = s->elem;

    return TSuccess;
}

static TStatus
GetDepth( Tint stid, Tint *depth )
{
    cmn_htbl_elem  rec;
    tsm_struct     s;
    Tint           i, d, b;
    tsm_elem       e;

    if( !stid_tbl )
	return TFailure; /* No structure created yet */

    /* Obtain structure pointer from hash table */
    rec = cmn_find_in_htbl( stid_tbl, stid, (void **)&s );
    if( !rec )
	return TFailure; /* Non-existent structure */

    (*depth)++;
    d = b = *depth;

    if( s )
    {
       e = s->elem;
       for( i = 0; i < s->header.num; i++ )
       {
	   if( e[i].el == TelExecuteStructure )
	   {
               GetDepth( e[i].data.ldata, &d );
               if( *depth < d )
	           *depth = d;
           }

           d = b;
       }
    }

    return TSuccess;
}


TStatus
TsmGetStructureDepth( Tint stid, Tint *depth )
{
    *depth = 0;

    GetDepth( stid, depth );
    return TSuccess;
}

TStatus
TsmGetCurElem( TSM_ELEM *elem )
{
   if( context.ind == TsmStructureNotOpen )
       return TFailure;

   *elem = context.n->elem;
   return TSuccess;
}

TStatus
TsmGetCurElemPtr( Tint *ptr )
{
   if( context.ind == TsmStructureNotOpen )
	  return TFailure;

   *ptr = context.ind;
   return TSuccess;
}
