/**************************************************************************
 * 
 * Copyright 2007 VMware, Inc.
 * All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 **************************************************************************/

#include "util/u_debug.h"
#include "pipe/p_format.h"
#include "pipe/p_shader_tokens.h"
#include "tgsi_build.h"
#include "tgsi_parse.h"


/*
 * header
 */

struct tgsi_header
tgsi_build_header( void )
{
   struct tgsi_header header;

   header.HeaderSize = 1;
   header.BodySize = 0;

   return header;
}

static void
header_headersize_grow( struct tgsi_header *header )
{
   assert( header->HeaderSize < 0xFF );
   assert( header->BodySize == 0 );

   header->HeaderSize++;
}

static void
header_bodysize_grow( struct tgsi_header *header )
{
   assert( header->BodySize < 0xFFFFFF );

   header->BodySize++;
}

struct tgsi_processor
tgsi_build_processor(
   unsigned type,
   struct tgsi_header *header )
{
   struct tgsi_processor processor;

   processor.Processor = type;
   processor.Padding = 0;

   header_headersize_grow( header );

   return processor;
}

/*
 * declaration
 */

static void
declaration_grow(
   struct tgsi_declaration *declaration,
   struct tgsi_header *header )
{
   assert( declaration->NrTokens < 0xFF );

   declaration->NrTokens++;

   header_bodysize_grow( header );
}

static struct tgsi_declaration
tgsi_default_declaration( void )
{
   struct tgsi_declaration declaration;

   declaration.Type = TGSI_TOKEN_TYPE_DECLARATION;
   declaration.NrTokens = 1;
   declaration.File = TGSI_FILE_NULL;
   declaration.UsageMask = TGSI_WRITEMASK_XYZW;
   declaration.Interpolate = 0;
   declaration.Dimension = 0;
   declaration.Semantic = 0;
   declaration.Invariant = 0;
   declaration.Local = 0;
   declaration.Array = 0;
   declaration.Padding = 0;

   return declaration;
}

static struct tgsi_declaration
tgsi_build_declaration(
   unsigned file,
   unsigned usage_mask,
   unsigned interpolate,
   unsigned dimension,
   unsigned semantic,
   unsigned invariant,
   unsigned local,
   unsigned array,
   struct tgsi_header *header )
{
   struct tgsi_declaration declaration;

   assert( file < TGSI_FILE_COUNT );
   assert( interpolate < TGSI_INTERPOLATE_COUNT );

   declaration = tgsi_default_declaration();
   declaration.File = file;
   declaration.UsageMask = usage_mask;
   declaration.Interpolate = interpolate;
   declaration.Dimension = dimension;
   declaration.Semantic = semantic;
   declaration.Invariant = invariant;
   declaration.Local = local;
   declaration.Array = array;
   header_bodysize_grow( header );

   return declaration;
}

static struct tgsi_declaration_range
tgsi_default_declaration_range( void )
{
   struct tgsi_declaration_range dr;

   dr.First = 0;
   dr.Last = 0;

   return dr;
}

static struct tgsi_declaration_range
tgsi_build_declaration_range(
   unsigned first,
   unsigned last,
   struct tgsi_declaration *declaration,
   struct tgsi_header *header )
{
   struct tgsi_declaration_range declaration_range;

   assert( last >= first );
   assert( last <= 0xFFFF );

   declaration_range.First = first;
   declaration_range.Last = last;

   declaration_grow( declaration, header );

   return declaration_range;
}

static struct tgsi_declaration_dimension
tgsi_build_declaration_dimension(unsigned index_2d,
                                 struct tgsi_declaration *declaration,
                                 struct tgsi_header *header)
{
   struct tgsi_declaration_dimension dd;

   assert(index_2d <= 0xFFFF);

   dd.Index2D = index_2d;
   dd.Padding = 0;

   declaration_grow(declaration, header);

   return dd;
}

static struct tgsi_declaration_interp
tgsi_default_declaration_interp( void )
{
   struct tgsi_declaration_interp di;

   di.Interpolate = TGSI_INTERPOLATE_CONSTANT;
   di.Centroid = 0;
   di.CylindricalWrap = 0;
   di.Padding = 0;

   return di;
}

static struct tgsi_declaration_interp
tgsi_build_declaration_interp(unsigned interpolate,
                              unsigned centroid,
                              unsigned cylindrical_wrap,
                              struct tgsi_declaration *declaration,
                              struct tgsi_header *header)
{
   struct tgsi_declaration_interp di;

   di.Interpolate = interpolate;
   di.Centroid = centroid;
   di.CylindricalWrap = cylindrical_wrap;
   di.Padding = 0;

   declaration_grow(declaration, header);

   return di;
}

static struct tgsi_declaration_semantic
tgsi_default_declaration_semantic( void )
{
   struct tgsi_declaration_semantic ds;

   ds.Name = TGSI_SEMANTIC_POSITION;
   ds.Index = 0;
   ds.Padding = 0;

   return ds;
}

static struct tgsi_declaration_semantic
tgsi_build_declaration_semantic(
   unsigned semantic_name,
   unsigned semantic_index,
   struct tgsi_declaration *declaration,
   struct tgsi_header *header )
{
   struct tgsi_declaration_semantic ds;

   assert( semantic_name <= TGSI_SEMANTIC_COUNT );
   assert( semantic_index <= 0xFFFF );

   ds.Name = semantic_name;
   ds.Index = semantic_index;
   ds.Padding = 0;

   declaration_grow( declaration, header );

   return ds;
}

static struct tgsi_declaration_resource
tgsi_default_declaration_resource(void)
{
   struct tgsi_declaration_resource dr;

   dr.Resource = TGSI_TEXTURE_BUFFER;
   dr.Raw = 0;
   dr.Writable = 0;
   dr.Padding = 0;

   return dr;
}

static struct tgsi_declaration_resource
tgsi_build_declaration_resource(unsigned texture,
                                unsigned raw,
                                unsigned writable,
                                struct tgsi_declaration *declaration,
                                struct tgsi_header *header)
{
   struct tgsi_declaration_resource dr;

   dr = tgsi_default_declaration_resource();
   dr.Resource = texture;
   dr.Raw = raw;
   dr.Writable = writable;

   declaration_grow(declaration, header);

   return dr;
}

static struct tgsi_declaration_sampler_view
tgsi_default_declaration_sampler_view(void)
{
   struct tgsi_declaration_sampler_view dsv;

   dsv.Resource = TGSI_TEXTURE_BUFFER;
   dsv.ReturnTypeX = PIPE_TYPE_UNORM;
   dsv.ReturnTypeY = PIPE_TYPE_UNORM;
   dsv.ReturnTypeZ = PIPE_TYPE_UNORM;
   dsv.ReturnTypeW = PIPE_TYPE_UNORM;

   return dsv;
}

static struct tgsi_declaration_sampler_view
tgsi_build_declaration_sampler_view(unsigned texture,
                                    unsigned return_type_x,
                                    unsigned return_type_y,
                                    unsigned return_type_z,
                                    unsigned return_type_w,
                                    struct tgsi_declaration *declaration,
                                    struct tgsi_header *header)
{
   struct tgsi_declaration_sampler_view dsv;

   dsv = tgsi_default_declaration_sampler_view();
   dsv.Resource = texture;
   dsv.ReturnTypeX = return_type_x;
   dsv.ReturnTypeY = return_type_y;
   dsv.ReturnTypeZ = return_type_z;
   dsv.ReturnTypeW = return_type_w;

   declaration_grow(declaration, header);

   return dsv;
}


static struct tgsi_declaration_array
tgsi_default_declaration_array( void )
{
   struct tgsi_declaration_array a;

   a.ArrayID = 0;
   a.Padding = 0;

   return a;
}

static struct tgsi_declaration_array
tgsi_build_declaration_array(unsigned arrayid,
                             struct tgsi_declaration *declaration,
                             struct tgsi_header *header)
{
   struct tgsi_declaration_array da;

   da = tgsi_default_declaration_array();
   da.ArrayID = arrayid;

   declaration_grow(declaration, header);

   return da;
}

struct tgsi_full_declaration
tgsi_default_full_declaration( void )
{
   struct tgsi_full_declaration  full_declaration;

   full_declaration.Declaration  = tgsi_default_declaration();
   full_declaration.Range = tgsi_default_declaration_range();
   full_declaration.Semantic = tgsi_default_declaration_semantic();
   full_declaration.Interp = tgsi_default_declaration_interp();
   full_declaration.Resource = tgsi_default_declaration_resource();
   full_declaration.SamplerView = tgsi_default_declaration_sampler_view();
   full_declaration.Array = tgsi_default_declaration_array();

   return full_declaration;
}

unsigned
tgsi_build_full_declaration(
   const struct tgsi_full_declaration *full_decl,
   struct tgsi_token *tokens,
   struct tgsi_header *header,
   unsigned maxsize )
{
   unsigned size = 0;
   struct tgsi_declaration *declaration;
   struct tgsi_declaration_range *dr;

   if( maxsize <= size )
      return 0;
   declaration = (struct tgsi_declaration *) &tokens[size];
   size++;

   *declaration = tgsi_build_declaration(
      full_decl->Declaration.File,
      full_decl->Declaration.UsageMask,
      full_decl->Declaration.Interpolate,
      full_decl->Declaration.Dimension,
      full_decl->Declaration.Semantic,
      full_decl->Declaration.Invariant,
      full_decl->Declaration.Local,
      full_decl->Declaration.Array,
      header );

   if (maxsize <= size)
      return 0;
   dr = (struct tgsi_declaration_range *) &tokens[size];
   size++;

   *dr = tgsi_build_declaration_range(
      full_decl->Range.First,
      full_decl->Range.Last,
      declaration,
      header );

   if (full_decl->Declaration.Dimension) {
      struct tgsi_declaration_dimension *dd;

      if (maxsize <= size) {
         return 0;
      }
      dd = (struct tgsi_declaration_dimension *)&tokens[size];
      size++;

      *dd = tgsi_build_declaration_dimension(full_decl->Dim.Index2D,
                                             declaration,
                                             header);
   }

   if (full_decl->Declaration.Interpolate) {
      struct tgsi_declaration_interp *di;

      if (maxsize <= size) {
         return 0;
      }
      di = (struct tgsi_declaration_interp *)&tokens[size];
      size++;

      *di = tgsi_build_declaration_interp(full_decl->Interp.Interpolate,
                                          full_decl->Interp.Centroid,
                                          full_decl->Interp.CylindricalWrap,
                                          declaration,
                                          header);
   }

   if( full_decl->Declaration.Semantic ) {
      struct tgsi_declaration_semantic *ds;

      if( maxsize <= size )
         return  0;
      ds = (struct tgsi_declaration_semantic *) &tokens[size];
      size++;

      *ds = tgsi_build_declaration_semantic(
         full_decl->Semantic.Name,
         full_decl->Semantic.Index,
         declaration,
         header );
   }

   if (full_decl->Declaration.File == TGSI_FILE_RESOURCE) {
      struct tgsi_declaration_resource *dr;

      if (maxsize <= size) {
         return  0;
      }
      dr = (struct tgsi_declaration_resource *)&tokens[size];
      size++;

      *dr = tgsi_build_declaration_resource(full_decl->Resource.Resource,
                                            full_decl->Resource.Raw,
                                            full_decl->Resource.Writable,
                                            declaration,
                                            header);
   }

   if (full_decl->Declaration.File == TGSI_FILE_SAMPLER_VIEW) {
      struct tgsi_declaration_sampler_view *dsv;

      if (maxsize <= size) {
         return  0;
      }
      dsv = (struct tgsi_declaration_sampler_view *)&tokens[size];
      size++;

      *dsv = tgsi_build_declaration_sampler_view(
         full_decl->SamplerView.Resource,
         full_decl->SamplerView.ReturnTypeX,
         full_decl->SamplerView.ReturnTypeY,
         full_decl->SamplerView.ReturnTypeZ,
         full_decl->SamplerView.ReturnTypeW,
         declaration,
         header);
   }

   if (full_decl->Declaration.Array) {
      struct tgsi_declaration_array *da;

      if (maxsize <= size) {
         return 0;
      }
      da = (struct tgsi_declaration_array *)&tokens[size];
      size++;
      *da = tgsi_build_declaration_array(
         full_decl->Array.ArrayID,
         declaration,
         header);
   }
   return size;
}

/*
 * immediate
 */

static struct tgsi_immediate
tgsi_default_immediate( void )
{
   struct tgsi_immediate immediate;

   immediate.Type = TGSI_TOKEN_TYPE_IMMEDIATE;
   immediate.NrTokens = 1;
   immediate.DataType = TGSI_IMM_FLOAT32;
   immediate.Padding = 0;

   return immediate;
}

static struct tgsi_immediate
tgsi_build_immediate(
   struct tgsi_header *header,
   unsigned type )
{
   struct tgsi_immediate immediate;

   immediate = tgsi_default_immediate();
   immediate.DataType = type;

   header_bodysize_grow( header );

   return immediate;
}

struct tgsi_full_immediate
tgsi_default_full_immediate( void )
{
   struct tgsi_full_immediate fullimm;

   fullimm.Immediate = tgsi_default_immediate();
   fullimm.u[0].Float = 0.0f;
   fullimm.u[1].Float = 0.0f;
   fullimm.u[2].Float = 0.0f;
   fullimm.u[3].Float = 0.0f;

   return fullimm;
}

static void
immediate_grow(
   struct tgsi_immediate *immediate,
   struct tgsi_header *header )
{
   assert( immediate->NrTokens < 0xFF );

   immediate->NrTokens++;

   header_bodysize_grow( header );
}

unsigned
tgsi_build_full_immediate(
   const struct tgsi_full_immediate *full_imm,
   struct tgsi_token *tokens,
   struct tgsi_header *header,
   unsigned maxsize )
{
   unsigned size = 0, i;
   struct tgsi_immediate *immediate;

   if( maxsize <= size )
      return 0;
   immediate = (struct tgsi_immediate *) &tokens[size];
   size++;

   *immediate = tgsi_build_immediate( header, full_imm->Immediate.DataType );

   assert( full_imm->Immediate.NrTokens <= 4 + 1 );

   for( i = 0; i < full_imm->Immediate.NrTokens - 1; i++ ) {
      union tgsi_immediate_data *data;

      if( maxsize <= size )
         return  0;

      data = (union tgsi_immediate_data *) &tokens[size];
      *data = full_imm->u[i];

      immediate_grow( immediate, header );
      size++;
   }

   return size;
}

/*
 * instruction
 */

struct tgsi_instruction
tgsi_default_instruction( void )
{
   struct tgsi_instruction instruction;

   instruction.Type = TGSI_TOKEN_TYPE_INSTRUCTION;
   instruction.NrTokens = 0;
   instruction.Opcode = TGSI_OPCODE_MOV;
   instruction.Saturate = TGSI_SAT_NONE;
   instruction.Predicate = 0;
   instruction.NumDstRegs = 1;
   instruction.NumSrcRegs = 1;
   instruction.Label = 0;
   instruction.Texture = 0;
   instruction.Padding  = 0;

   return instruction;
}

static struct tgsi_instruction
tgsi_build_instruction(unsigned opcode,
                       unsigned saturate,
                       unsigned predicate,
                       unsigned num_dst_regs,
                       unsigned num_src_regs,
                       struct tgsi_header *header)
{
   struct tgsi_instruction instruction;

   assert (opcode <= TGSI_OPCODE_LAST);
   assert (saturate <= TGSI_SAT_MINUS_PLUS_ONE);
   assert (num_dst_regs <= 3);
   assert (num_src_regs <= 15);

   instruction = tgsi_default_instruction();
   instruction.Opcode = opcode;
   instruction.Saturate = saturate;
   instruction.Predicate = predicate;
   instruction.NumDstRegs = num_dst_regs;
   instruction.NumSrcRegs = num_src_regs;

   header_bodysize_grow( header );

   return instruction;
}

static void
instruction_grow(
   struct tgsi_instruction *instruction,
   struct tgsi_header *header )
{
   assert (instruction->NrTokens <   0xFF);

   instruction->NrTokens++;

   header_bodysize_grow( header );
}

struct tgsi_instruction_predicate
tgsi_default_instruction_predicate(void)
{
   struct tgsi_instruction_predicate instruction_predicate;

   instruction_predicate.SwizzleX = TGSI_SWIZZLE_X;
   instruction_predicate.SwizzleY = TGSI_SWIZZLE_Y;
   instruction_predicate.SwizzleZ = TGSI_SWIZZLE_Z;
   instruction_predicate.SwizzleW = TGSI_SWIZZLE_W;
   instruction_predicate.Negate = 0;
   instruction_predicate.Index = 0;
   instruction_predicate.Padding = 0;

   return instruction_predicate;
}

static struct tgsi_instruction_predicate
tgsi_build_instruction_predicate(int index,
                                 unsigned negate,
                                 unsigned swizzleX,
                                 unsigned swizzleY,
                                 unsigned swizzleZ,
                                 unsigned swizzleW,
                                 struct tgsi_instruction *instruction,
                                 struct tgsi_header *header)
{
   struct tgsi_instruction_predicate instruction_predicate;

   instruction_predicate = tgsi_default_instruction_predicate();
   instruction_predicate.SwizzleX = swizzleX;
   instruction_predicate.SwizzleY = swizzleY;
   instruction_predicate.SwizzleZ = swizzleZ;
   instruction_predicate.SwizzleW = swizzleW;
   instruction_predicate.Negate = negate;
   instruction_predicate.Index = index;

   instruction_grow(instruction, header);

   return instruction_predicate;
}

static struct tgsi_instruction_label
tgsi_default_instruction_label( void )
{
   struct tgsi_instruction_label instruction_label;

   instruction_label.Label = 0;
   instruction_label.Padding = 0;

   return instruction_label;
}

static struct tgsi_instruction_label
tgsi_build_instruction_label(
   unsigned label,
   struct tgsi_token  *prev_token,
   struct tgsi_instruction *instruction,
   struct tgsi_header *header )
{
   struct tgsi_instruction_label instruction_label;

   instruction_label.Label = label;
   instruction_label.Padding = 0;
   instruction->Label = 1;

   instruction_grow( instruction, header );

   return instruction_label;
}

static struct tgsi_instruction_texture
tgsi_default_instruction_texture( void )
{
   struct tgsi_instruction_texture instruction_texture;

   instruction_texture.Texture = TGSI_TEXTURE_UNKNOWN;
   instruction_texture.NumOffsets = 0;
   instruction_texture.Padding = 0;

   return instruction_texture;
}

static struct tgsi_instruction_texture
tgsi_build_instruction_texture(
   unsigned texture,
   unsigned num_offsets,
   struct tgsi_token *prev_token,
   struct tgsi_instruction *instruction,
   struct tgsi_header *header )
{
   struct tgsi_instruction_texture instruction_texture;

   instruction_texture.Texture = texture;
   instruction_texture.NumOffsets = num_offsets;
   instruction_texture.Padding = 0;
   instruction->Texture = 1;

   instruction_grow( instruction, header );

   return instruction_texture;
}


static struct tgsi_texture_offset
tgsi_default_texture_offset( void )
{
   struct tgsi_texture_offset texture_offset;

   texture_offset.Index = 0;
   texture_offset.File = 0;
   texture_offset.SwizzleX = 0;
   texture_offset.SwizzleY = 0;
   texture_offset.SwizzleZ = 0;
   texture_offset.Padding = 0;

   return texture_offset;
}

static struct tgsi_texture_offset
tgsi_build_texture_offset(
   int index, int file, int swizzle_x, int swizzle_y, int swizzle_z,
   struct tgsi_token *prev_token,
   struct tgsi_instruction *instruction,
   struct tgsi_header *header )
{
   struct tgsi_texture_offset texture_offset;

   texture_offset.Index = index;
   texture_offset.File = file;
   texture_offset.SwizzleX = swizzle_x;
   texture_offset.SwizzleY = swizzle_y;
   texture_offset.SwizzleZ = swizzle_z;
   texture_offset.Padding = 0;

   instruction_grow( instruction, header );

   return texture_offset;
}

static struct tgsi_src_register
tgsi_default_src_register( void )
{
   struct tgsi_src_register src_register;

   src_register.File = TGSI_FILE_NULL;
   src_register.SwizzleX = TGSI_SWIZZLE_X;
   src_register.SwizzleY = TGSI_SWIZZLE_Y;
   src_register.SwizzleZ = TGSI_SWIZZLE_Z;
   src_register.SwizzleW = TGSI_SWIZZLE_W;
   src_register.Negate = 0;
   src_register.Absolute = 0;
   src_register.Indirect = 0;
   src_register.Dimension = 0;
   src_register.Index = 0;

   return src_register;
}

static struct tgsi_src_register
tgsi_build_src_register(
   unsigned file,
   unsigned swizzle_x,
   unsigned swizzle_y,
   unsigned swizzle_z,
   unsigned swizzle_w,
   unsigned negate,
   unsigned absolute,
   unsigned indirect,
   unsigned dimension,
   int index,
   struct tgsi_instruction *instruction,
   struct tgsi_header *header )
{
   struct tgsi_src_register   src_register;

   assert( file < TGSI_FILE_COUNT );
   assert( swizzle_x <= TGSI_SWIZZLE_W );
   assert( swizzle_y <= TGSI_SWIZZLE_W );
   assert( swizzle_z <= TGSI_SWIZZLE_W );
   assert( swizzle_w <= TGSI_SWIZZLE_W );
   assert( negate <= 1 );
   assert( index >= -0x8000 && index <= 0x7FFF );

   src_register.File = file;
   src_register.SwizzleX = swizzle_x;
   src_register.SwizzleY = swizzle_y;
   src_register.SwizzleZ = swizzle_z;
   src_register.SwizzleW = swizzle_w;
   src_register.Negate = negate;
   src_register.Absolute = absolute;
   src_register.Indirect = indirect;
   src_register.Dimension = dimension;
   src_register.Index = index;

   instruction_grow( instruction, header );

   return src_register;
}

static struct tgsi_ind_register
tgsi_default_ind_register( void )
{
   struct tgsi_ind_register ind_register;

   ind_register.File = TGSI_FILE_NULL;
   ind_register.Index = 0;
   ind_register.Swizzle = TGSI_SWIZZLE_X;
   ind_register.ArrayID = 0;

   return ind_register;
}

static struct tgsi_ind_register
tgsi_build_ind_register(
   unsigned file,
   unsigned swizzle,
   int index,
   unsigned arrayid,
   struct tgsi_instruction *instruction,
   struct tgsi_header *header )
{
   struct tgsi_ind_register   ind_register;

   assert( file < TGSI_FILE_COUNT );
   assert( swizzle <= TGSI_SWIZZLE_W );
   assert( index >= -0x8000 && index <= 0x7FFF );

   ind_register.File = file;
   ind_register.Swizzle = swizzle;
   ind_register.Index = index;
   ind_register.ArrayID = arrayid;

   instruction_grow( instruction, header );

   return ind_register;
}

static struct tgsi_dimension
tgsi_default_dimension( void )
{
   struct tgsi_dimension dimension;

   dimension.Indirect = 0;
   dimension.Dimension = 0;
   dimension.Padding = 0;
   dimension.Index = 0;

   return dimension;
}

static struct tgsi_full_src_register
tgsi_default_full_src_register( void )
{
   struct tgsi_full_src_register full_src_register;

   full_src_register.Register = tgsi_default_src_register();
   full_src_register.Indirect = tgsi_default_ind_register();
   full_src_register.Dimension = tgsi_default_dimension();
   full_src_register.DimIndirect = tgsi_default_ind_register();

   return full_src_register;
}

static struct tgsi_dimension
tgsi_build_dimension(
   unsigned indirect,
   unsigned index,
   struct tgsi_instruction *instruction,
   struct tgsi_header *header )
{
   struct tgsi_dimension dimension;

   dimension.Indirect = indirect;
   dimension.Dimension = 0;
   dimension.Padding = 0;
   dimension.Index = index;

   instruction_grow( instruction, header );

   return dimension;
}

static struct tgsi_dst_register
tgsi_default_dst_register( void )
{
   struct tgsi_dst_register dst_register;

   dst_register.File = TGSI_FILE_NULL;
   dst_register.WriteMask = TGSI_WRITEMASK_XYZW;
   dst_register.Indirect = 0;
   dst_register.Dimension = 0;
   dst_register.Index = 0;
   dst_register.Padding = 0;

   return dst_register;
}

static struct tgsi_dst_register
tgsi_build_dst_register(
   unsigned file,
   unsigned mask,
   unsigned indirect,
   unsigned dimension,
   int index,
   struct tgsi_instruction *instruction,
   struct tgsi_header *header )
{
   struct tgsi_dst_register dst_register;

   assert( file < TGSI_FILE_COUNT );
   assert( mask <= TGSI_WRITEMASK_XYZW );
   assert( index >= -32768 && index <= 32767 );

   dst_register.File = file;
   dst_register.WriteMask = mask;
   dst_register.Indirect = indirect;
   dst_register.Dimension = dimension;
   dst_register.Index = index;
   dst_register.Padding = 0;

   instruction_grow( instruction, header );

   return dst_register;
}

static struct tgsi_full_dst_register
tgsi_default_full_dst_register( void )
{
   struct tgsi_full_dst_register full_dst_register;

   full_dst_register.Register = tgsi_default_dst_register();
   full_dst_register.Indirect = tgsi_default_ind_register();
   full_dst_register.Dimension = tgsi_default_dimension();
   full_dst_register.DimIndirect = tgsi_default_ind_register();

   return full_dst_register;
}

struct tgsi_full_instruction
tgsi_default_full_instruction( void )
{
   struct tgsi_full_instruction full_instruction;
   unsigned i;

   full_instruction.Instruction = tgsi_default_instruction();
   full_instruction.Predicate = tgsi_default_instruction_predicate();
   full_instruction.Label = tgsi_default_instruction_label();
   full_instruction.Texture = tgsi_default_instruction_texture();
   for( i = 0;  i < TGSI_FULL_MAX_TEX_OFFSETS; i++ ) {
      full_instruction.TexOffsets[i] = tgsi_default_texture_offset();
   }
   for( i = 0;  i < TGSI_FULL_MAX_DST_REGISTERS; i++ ) {
      full_instruction.Dst[i] = tgsi_default_full_dst_register();
   }
   for( i = 0;  i < TGSI_FULL_MAX_SRC_REGISTERS; i++ ) {
      full_instruction.Src[i] = tgsi_default_full_src_register();
   }

   return full_instruction;
}

unsigned
tgsi_build_full_instruction(
   const struct tgsi_full_instruction *full_inst,
   struct  tgsi_token *tokens,
   struct  tgsi_header *header,
   unsigned  maxsize )
{
   unsigned size = 0;
   unsigned i;
   struct tgsi_instruction *instruction;
   struct tgsi_token *prev_token;

   if( maxsize <= size )
      return 0;
   instruction = (struct tgsi_instruction *) &tokens[size];
   size++;

   *instruction = tgsi_build_instruction(full_inst->Instruction.Opcode,
                                         full_inst->Instruction.Saturate,
                                         full_inst->Instruction.Predicate,
                                         full_inst->Instruction.NumDstRegs,
                                         full_inst->Instruction.NumSrcRegs,
                                         header);
   prev_token = (struct tgsi_token  *) instruction;

   if (full_inst->Instruction.Predicate) {
      struct tgsi_instruction_predicate *instruction_predicate;

      if (maxsize <= size) {
         return 0;
      }
      instruction_predicate = (struct tgsi_instruction_predicate *)&tokens[size];
      size++;

      *instruction_predicate =
         tgsi_build_instruction_predicate(full_inst->Predicate.Index,
                                          full_inst->Predicate.Negate,
                                          full_inst->Predicate.SwizzleX,
                                          full_inst->Predicate.SwizzleY,
                                          full_inst->Predicate.SwizzleZ,
                                          full_inst->Predicate.SwizzleW,
                                          instruction,
                                          header);
   }

   if (full_inst->Instruction.Label) {
      struct tgsi_instruction_label *instruction_label;

      if( maxsize <= size )
         return 0;
      instruction_label =
         (struct  tgsi_instruction_label *) &tokens[size];
      size++;

      *instruction_label = tgsi_build_instruction_label(
         full_inst->Label.Label,
         prev_token,
         instruction,
         header );
      prev_token = (struct tgsi_token  *) instruction_label;
   }

   if (full_inst->Instruction.Texture) {
      struct tgsi_instruction_texture *instruction_texture;

      if( maxsize <= size )
         return 0;
      instruction_texture =
         (struct  tgsi_instruction_texture *) &tokens[size];
      size++;

      *instruction_texture = tgsi_build_instruction_texture(
         full_inst->Texture.Texture,
	 full_inst->Texture.NumOffsets,
         prev_token,
         instruction,
         header   );
      prev_token = (struct tgsi_token  *) instruction_texture;

      for (i = 0; i < full_inst->Texture.NumOffsets; i++) {
         struct tgsi_texture_offset *texture_offset;
	
         if ( maxsize <= size )
            return 0;
	 texture_offset = (struct tgsi_texture_offset *)&tokens[size];
         size++;
         *texture_offset = tgsi_build_texture_offset(
            full_inst->TexOffsets[i].Index,
            full_inst->TexOffsets[i].File,
            full_inst->TexOffsets[i].SwizzleX,
            full_inst->TexOffsets[i].SwizzleY,
            full_inst->TexOffsets[i].SwizzleZ,
            prev_token,
            instruction,
            header);
         prev_token = (struct tgsi_token *) texture_offset;
      }
   }
   for( i = 0;  i <   full_inst->Instruction.NumDstRegs; i++ ) {
      const struct tgsi_full_dst_register *reg = &full_inst->Dst[i];
      struct tgsi_dst_register *dst_register;

      if( maxsize <= size )
         return 0;
      dst_register = (struct tgsi_dst_register *) &tokens[size];
      size++;

      *dst_register = tgsi_build_dst_register(
         reg->Register.File,
         reg->Register.WriteMask,
         reg->Register.Indirect,
         reg->Register.Dimension,
         reg->Register.Index,
         instruction,
         header );

      if( reg->Register.Indirect ) {
         struct tgsi_ind_register *ind;

         if( maxsize <= size )
            return 0;
         ind = (struct tgsi_ind_register *) &tokens[size];
         size++;

         *ind = tgsi_build_ind_register(
            reg->Indirect.File,
            reg->Indirect.Swizzle,
            reg->Indirect.Index,
            reg->Indirect.ArrayID,
            instruction,
            header );
      }

      if( reg->Register.Dimension ) {
         struct  tgsi_dimension *dim;

         assert( !reg->Dimension.Dimension );

         if( maxsize <= size )
            return 0;
         dim = (struct tgsi_dimension *) &tokens[size];
         size++;

         *dim = tgsi_build_dimension(
            reg->Dimension.Indirect,
            reg->Dimension.Index,
            instruction,
            header );

         if( reg->Dimension.Indirect ) {
            struct tgsi_ind_register *ind;

            if( maxsize <= size )
               return 0;
            ind = (struct tgsi_ind_register *) &tokens[size];
            size++;

            *ind = tgsi_build_ind_register(
               reg->DimIndirect.File,
               reg->DimIndirect.Swizzle,
               reg->DimIndirect.Index,
               reg->DimIndirect.ArrayID,
               instruction,
               header );
         }
      }
   }

   for( i = 0;  i < full_inst->Instruction.NumSrcRegs; i++ ) {
      const struct tgsi_full_src_register *reg = &full_inst->Src[i];
      struct tgsi_src_register *src_register;

      if( maxsize <= size )
         return 0;
      src_register = (struct tgsi_src_register *)  &tokens[size];
      size++;

      *src_register = tgsi_build_src_register(
         reg->Register.File,
         reg->Register.SwizzleX,
         reg->Register.SwizzleY,
         reg->Register.SwizzleZ,
         reg->Register.SwizzleW,
         reg->Register.Negate,
         reg->Register.Absolute,
         reg->Register.Indirect,
         reg->Register.Dimension,
         reg->Register.Index,
         instruction,
         header );

      if( reg->Register.Indirect ) {
         struct  tgsi_ind_register *ind;

         if( maxsize <= size )
            return 0;
         ind = (struct tgsi_ind_register *) &tokens[size];
         size++;

         *ind = tgsi_build_ind_register(
            reg->Indirect.File,
            reg->Indirect.Swizzle,
            reg->Indirect.Index,
            reg->Indirect.ArrayID,
            instruction,
            header );
      }

      if( reg->Register.Dimension ) {
         struct  tgsi_dimension *dim;

         assert( !reg->Dimension.Dimension );

         if( maxsize <= size )
            return 0;
         dim = (struct tgsi_dimension *) &tokens[size];
         size++;

         *dim = tgsi_build_dimension(
            reg->Dimension.Indirect,
            reg->Dimension.Index,
            instruction,
            header );

         if( reg->Dimension.Indirect ) {
            struct tgsi_ind_register *ind;

            if( maxsize <= size )
               return 0;
            ind = (struct tgsi_ind_register *) &tokens[size];
            size++;

            *ind = tgsi_build_ind_register(
               reg->DimIndirect.File,
               reg->DimIndirect.Swizzle,
               reg->DimIndirect.Index,
               reg->DimIndirect.ArrayID,
               instruction,
               header );
         }
      }
   }

   return size;
}

static struct tgsi_property
tgsi_default_property( void )
{
   struct tgsi_property property;

   property.Type = TGSI_TOKEN_TYPE_PROPERTY;
   property.NrTokens = 1;
   property.PropertyName = TGSI_PROPERTY_GS_INPUT_PRIM;
   property.Padding = 0;

   return property;
}

static struct tgsi_property
tgsi_build_property(unsigned property_name,
                    struct tgsi_header *header)
{
   struct tgsi_property property;

   property = tgsi_default_property();
   property.PropertyName = property_name;

   header_bodysize_grow( header );

   return property;
}


struct tgsi_full_property
tgsi_default_full_property( void )
{
   struct tgsi_full_property  full_property;

   full_property.Property  = tgsi_default_property();
   memset(full_property.u, 0,
          sizeof(struct tgsi_property_data) * 8);

   return full_property;
}

static void
property_grow(
   struct tgsi_property *property,
   struct tgsi_header *header )
{
   assert( property->NrTokens < 0xFF );

   property->NrTokens++;

   header_bodysize_grow( header );
}

static struct tgsi_property_data
tgsi_build_property_data(
   unsigned value,
   struct tgsi_property *property,
   struct tgsi_header *header )
{
   struct tgsi_property_data property_data;

   property_data.Data = value;

   property_grow( property, header );

   return property_data;
}

unsigned
tgsi_build_full_property(
   const struct tgsi_full_property *full_prop,
   struct tgsi_token *tokens,
   struct tgsi_header *header,
   unsigned maxsize )
{
   unsigned size = 0, i;
   struct tgsi_property *property;

   if( maxsize <= size )
      return 0;
   property = (struct tgsi_property *) &tokens[size];
   size++;

   *property = tgsi_build_property(
      full_prop->Property.PropertyName,
      header );

   assert( full_prop->Property.NrTokens <= 8 + 1 );

   for( i = 0; i < full_prop->Property.NrTokens - 1; i++ ) {
      struct tgsi_property_data *data;

      if( maxsize <= size )
         return  0;
      data = (struct tgsi_property_data *) &tokens[size];
      size++;

      *data = tgsi_build_property_data(
         full_prop->u[i].Data,
         property,
         header );
   }

   return size;
}
