#define PEX_TXM_NUM_TEXELS(_domain, _level, _texel_arrays )           \
        ( (PEXEnumTypeIndex)_domain == PEXExtTMDomainColor1D ?          \
	                  _texel_arrays[_level].dimension.t0 :          \
        ( (PEXEnumTypeIndex)_domain == PEXExtTMDomainColor2D ?          \
			  _texel_arrays[_level].dimension.t0 *          \
			  _texel_arrays[_level].dimension.t1 :          \
			  _texel_arrays[_level].dimension.t0 *          \
			  _texel_arrays[_level].dimension.t1 *          \
			  _texel_arrays[_level].dimension.t2 ))

typedef struct {
	INT16           float_format;
	CARD16          num_arrays;
	CARD32          tm_id;
	INT16           tm_domain;
	INT16           tm_type;
	INT16           texel_type;
	CARD16          num_levels;
	CARD32          texel_array_size;
	/* variable list of domain data and
	   texel data follows here in reply data */
} pexExtEscapeCreateTMData;

typedef struct {
	CARD16	t0;
	CARD16	t1;
	CARD16	t2;
	CARD16  unused;
} pexExtTexelDimension;

 /****************************************************************************
 ** Global variable to use when receiving large texture maps in chunks. Since
 ** the entire texture map is not contained in one packet this global
 ** is needed to allow additions.
 *****************************************************************************
 */


PEXExtTextureMap PEXExtCreateTM (
INPUT			Display 		*display,
INPUT			int			domain,
INPUT			PEXExtTMDomainData	*domain_data,
INPUT			PEXExtTexelArray	*texel_arrays)
/*
*******************************************************************************
**    Description:
**          This function saves the given texture map and it's domain 
**          description returning an id that can be used for future 
**          reference. DHA or protocol connections are handled automatically.
**
**
**    Input parameters:
**          display              -Display structure from XOpenDisplay
**          domain               -domain type of data, currently supported:
**		                                     PEXExtTMDomainColor1D, 
**		                                     PEXExtTMDomainColor2D,
**		                                     PEXExtTMDomainColor3D
**          domain_data          -domain specific data record
**          texel_arrays         -list of texel arrays describing texture map
**
**    Output parameters:
**          Returns a PEXExtTextureMap reference identifier, if sucessful.
**
**    Entry Conditions:
**          1) Assumes pointers reference data conforming to PEX 5.1v2 spec 
**	       TM features that are supported.
**    Exit Conditions:
**          1) Internal resource allocated and initialized with texel data.
**          2) Resource identifier returned if sucessful, 0 on failure.
**
**    Error Conditions:
**             When possible errors are deferred to the lowest level
**             to support future extensions using the imp dep features. 
**          1) BadAlloc if unable to allocate resource block
**         
**    Algorithm:
**          1) Get an id from the X display
**          2) Find the maximum transfer size the display supports and adjust
**	       for header size and word alignment.
**          3) Find the sizes of data and total bytes to send.
**          4) Allocate transport memory that will be filled with protocol.
**          5) Transfer the given create data to start of protocol memory. 
**          6) Loop on levels of texels, transfering  them to transport memory
**             until data is exhausted.
**          7) Send the first full block of data with CreateTM escape, 
**             emptying memory.
**          8) Send further full blocks with CreateTMExtraData escape, 
**             emptying memory.
**
**    Referenced by:
**          Globally available.
**
**    Procedure/FunctionDependencies:
**
**    Comments:
**          Several changes have been accepted since the original spec was
**          published. 
**          A) The size of the texel data was added to the create structure.
**          B) The TM id is the first entry in CreateTMExtraData data.
**          C) Texels are padded in protocol the same as in API. 
**          D) Very large TMs are possible because chunks can be sent. 
**          E) The data structure pexExtDomainColorData is no longer needed.
**
**    Change History:
**          31 Jan 94 - Dave Luttropp created to handle large blocks
*******************************************************************************
*/

    {

      int                       copy_size;            /* All these sizes are */
      int			create_byte_size;     /* in protocol bytes   */
      int                       dimension_byte_size;
      int                       levels_texel_bytes;
      int                       max_transfer_size;
      int                       space_available;
      int                       texel_bytes_sent;
      int			texel_byte_size;
      int			total_byte_size;       
      int 			transfer_size;

      char			*escape_data;        /* protocol variables */
      pexExtEscapeCreateTMData	*req;
      pexExtTexelDimension      *dim_level;
      char                      *pbuf;

      unsigned short int	num_levels;          /* Misc variables     */
      int                       num_texels;
      unsigned short int	domain_is_color;
      PEXExtTextureMap		tm_ident;
      int			i;
      int                       first_time = True;
      int                       fp_convert;
      int                       fp_format;

      /*
	 Get a unique identifier from the display to label this texture map
      */
      tm_ident = XAllocID( display );

      /* 
	 Determine size of maximum transfer adjusting to word size. 
	 Note: The maximum size is reduced by the header as specified
	 by XMaxRequestSize()
      */
      max_transfer_size = (XMaxRequestSize(display) - 4 );
      max_transfer_size += PAD(max_transfer_size) -4;

      domain_is_color = ( (PEXEnumTypeIndex)domain == PEXExtTMDomainColor1D ||
		          (PEXEnumTypeIndex)domain == PEXExtTMDomainColor2D ||
		          (PEXEnumTypeIndex)domain == PEXExtTMDomainColor3D ) ?
		          1:0;
      if( !domain_is_color ){
	 return 0;
      }

      num_levels = (domain_is_color) ? domain_data->data.color.num_levels : 0;
      dimension_byte_size = SIZEOF( pexExtTexelDimension );

      /*
	 Check for a texel type in the defined range
      */

      if( domain_data->data.color.texel_type > PEXExtTexelRGBAlphaInt16 ||
	  domain_data->data.color.texel_type < PEXExtTexelLuminanceFloat ){
	  return 0;
      }

      num_texels = 0;
      texel_byte_size = 0;

      if( domain_is_color ){
	for(i=0; i<num_levels; i++)
           num_texels += PEX_TXM_NUM_TEXELS(domain, i, texel_arrays);

           texel_byte_size = (num_levels * dimension_byte_size) +
			    PADDED_BYTES(num_texels *
		            GetTexelSize(domain_data->data.color.texel_type));
       }


     create_byte_size = SIZEOF(pexExtEscapeCreateTMData);

     total_byte_size = texel_byte_size + create_byte_size;

     transfer_size = (total_byte_size > max_transfer_size)? 
					 max_transfer_size:
					 total_byte_size;

     /*
	Create a memory block to hold protocol data
     */
     escape_data = PEXAllocBuf( transfer_size ); 

     if(escape_data == NULL){
	return 0;
	}

     fp_format = PEXGetProtocolFloatFormat( display );
     fp_convert = (fp_format != NATIVE_FP_FORMAT);
     /* 
	Create TM data loaded into the protocol structure
     */
     req = (pexExtEscapeCreateTMData *)escape_data;
     req->float_format = fp_format;
     req->tm_domain = domain;
     req->tm_id = tm_ident;
     req->num_arrays = num_levels;
     req->texel_array_size = texel_byte_size;
     req->texel_type = domain_data->data.color.texel_type;
     req->num_levels = num_levels;
     req->tm_type = domain_data->data.color.tm_type;

     if( req->tm_type != PEXExtTMTypeMipMap ){
	free(escape_data);
	return 0;
     }

     /* 
	Texels are loaded one level at a time since the arrays may
	not be in contigous memory. When the buffer is full the texels
	are sent and the buffer is flushed.
     */
	texel_bytes_sent = 0;
	space_available = transfer_size - create_byte_size;
	pbuf = escape_data + create_byte_size;

	for(i=0; i< num_levels; i++)
	{

        num_texels =PEX_TXM_NUM_TEXELS(domain, i, texel_arrays );
	   
        levels_texel_bytes = dimension_byte_size +
			     num_texels * 
			     GetTexelSize( domain_data->data.color.texel_type );

        dim_level = (pexExtTexelDimension *)pbuf;
	dim_level->t0 = texel_arrays[i].dimension.t0;
	dim_level->t1 = texel_arrays[i].dimension.t1;
	dim_level->t2 = texel_arrays[i].dimension.t2;

	/*
	   Check on out of range dimensions
	*/
	if( dim_level->t0 <= 0 || dim_level->t1 <= 0 || dim_level->t2 <= 0 ){
	    free(escape_data);
	    return 0;
	}

	levels_texel_bytes -= dimension_byte_size;
	texel_bytes_sent += dimension_byte_size;
	space_available -= dimension_byte_size;
	pbuf += dimension_byte_size;

	/*
	   Loop on this level until all texels have been loaded in buffer
        */
	while( levels_texel_bytes > 0 ){
	   copy_size = (levels_texel_bytes > space_available )?
					       space_available:
					    levels_texel_bytes;


           COPY_AREA(texel_arrays[i].array.luminance, pbuf, copy_size );
	   space_available -= copy_size;
	   levels_texel_bytes -= copy_size;
	   texel_bytes_sent += copy_size;
	   pbuf += copy_size;

	   /*
	      Send Escape when the buffer is full or at end of texels 
           */

	   if(( space_available <=0 )||(texel_byte_size == texel_bytes_sent)){
	       if( first_time ){
		  PEXEscape(display, PEXExtEscapeOpcodeCreateTM,
			       pbuf - escape_data, (char *)escape_data);
                  first_time = False;
		}
	        else {
		  PEXEscape(display, PEXExtEscapeOpcodeCreateTMExtraData,
			       pbuf - escape_data, (char *)escape_data);
                }
               
	       pbuf = escape_data;
	       COPY_AREA( &tm_ident, pbuf, PADDED_BYTES(SIZEOF(PEXExtTextureMap)));
	       pbuf += PADDED_BYTES(SIZEOF(PEXExtTextureMap));
	       space_available = transfer_size - 
				 dimension_byte_size - 
				 PADDED_BYTES(SIZEOF(PEXExtTextureMap));

            }
	} 
        }

     PEXFreeBuf(escape_data); 
     return (tm_ident);
  }
} /* PEXExtCreateTM */