/**********************************************************
 * Copyright 2008-2009 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, sublicense, 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 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
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * 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 "pipe/p_shader_tokens.h"
#include "tgsi/tgsi_parse.h"
#include "util/u_memory.h"

#include "svga_tgsi_emit.h"


static boolean ps20_input( struct svga_shader_emitter *emit,
                           struct tgsi_declaration_semantic semantic,
                           unsigned idx )
{
   struct src_register reg;
   SVGA3DOpDclArgs dcl;
   SVGA3dShaderInstToken opcode;

   opcode = inst_token( SVGA3DOP_DCL );
   dcl.values[0] = 0;
   dcl.values[1] = 0;

   switch (semantic.Name) {
   case TGSI_SEMANTIC_POSITION:
      /* Special case:
       */
      reg = src_register( SVGA3DREG_MISCTYPE, 
                          SVGA3DMISCREG_POSITION );
      break;
   case TGSI_SEMANTIC_COLOR:
      reg = src_register( SVGA3DREG_INPUT, 
                          semantic.Index );
      break;
   case TGSI_SEMANTIC_FOG:
      assert(semantic.Index == 0);
      reg = src_register( SVGA3DREG_TEXTURE, 0 );
      break;
   case TGSI_SEMANTIC_GENERIC:
      reg = src_register( SVGA3DREG_TEXTURE,
                          semantic.Index + 1 );
      break;
   default:
      assert(0);
      return TRUE;
   }

   emit->input_map[idx] = reg;

   dcl.dst = dst( reg );

   dcl.usage = 0;
   dcl.index = 0;

   dcl.values[0] |= 1<<31;

   return  (emit_instruction(emit, opcode) &&
            svga_shader_emit_dwords( emit, dcl.values, Elements(dcl.values)));
}


static boolean ps20_output( struct svga_shader_emitter *emit,
                            struct tgsi_declaration_semantic semantic,
                            unsigned idx )
{
   SVGA3dShaderDestToken reg;

   switch (semantic.Name) {
   case TGSI_SEMANTIC_COLOR:
      if (semantic.Index < PIPE_MAX_COLOR_BUFS) {
         unsigned cbuf = semantic.Index;

         emit->output_map[idx] = dst_register( SVGA3DREG_TEMP,
                                               emit->nr_hw_temp++ );
         emit->temp_col[cbuf] = emit->output_map[idx];
         emit->true_col[cbuf] = dst_register( SVGA3DREG_COLOROUT, 
                                              semantic.Index );
      }
      else {
         assert(0);
         reg = dst_register( SVGA3DREG_COLOROUT, 0 );
      }
      break;
   case TGSI_SEMANTIC_POSITION:
      emit->output_map[idx] = dst_register( SVGA3DREG_TEMP,
                                            emit->nr_hw_temp++ );
      emit->temp_pos = emit->output_map[idx];
      emit->true_pos = dst_register( SVGA3DREG_DEPTHOUT, 
                                     semantic.Index );
      break;
   default:
      assert(0);
      reg = dst_register( SVGA3DREG_COLOROUT, 0 );
      break;
   }

   return TRUE;
}


static boolean vs20_input( struct svga_shader_emitter *emit,
                           struct tgsi_declaration_semantic semantic,
                           unsigned idx )
{
   SVGA3DOpDclArgs dcl;
   SVGA3dShaderInstToken opcode;

   opcode = inst_token( SVGA3DOP_DCL );
   dcl.values[0] = 0;
   dcl.values[1] = 0;

   emit->input_map[idx] = src_register( SVGA3DREG_INPUT, idx );
   dcl.dst = dst_register( SVGA3DREG_INPUT, idx );

   assert(dcl.dst.reserved0);

   /* Mesa doesn't provide use with VS input semantics (they're
    * actually pretty meaningless), so we just generate some plausible
    * ones here.  This has to match what we declare in the vdecl code
    * in svga_pipe_vertex.c.
    */
   if (idx == 0) {
      dcl.usage = SVGA3D_DECLUSAGE_POSITION;
      dcl.index = 0;
   }
   else {
      dcl.usage = SVGA3D_DECLUSAGE_TEXCOORD;
      dcl.index = idx - 1;
   }

   dcl.values[0] |= 1<<31;

   return  (emit_instruction(emit, opcode) &&
            svga_shader_emit_dwords( emit, dcl.values, Elements(dcl.values)));
}


static boolean vs20_output( struct svga_shader_emitter *emit,
                         struct tgsi_declaration_semantic semantic,
                         unsigned idx )
{
   /* Don't emit dcl instruction for vs20 inputs
    */

   /* Just build the register map table: 
    */
   switch (semantic.Name) {
   case TGSI_SEMANTIC_POSITION:
      assert(semantic.Index == 0);
      emit->output_map[idx] = dst_register( SVGA3DREG_TEMP,
                                            emit->nr_hw_temp++ );
      emit->temp_pos = emit->output_map[idx];
      emit->true_pos = dst_register( SVGA3DREG_RASTOUT, 
                                     SVGA3DRASTOUT_POSITION);
      break;
   case TGSI_SEMANTIC_PSIZE:
      assert(semantic.Index == 0);
      emit->output_map[idx] = dst_register( SVGA3DREG_TEMP,
                                            emit->nr_hw_temp++ );
      emit->temp_psiz = emit->output_map[idx];
      emit->true_psiz = dst_register( SVGA3DREG_RASTOUT, 
                                      SVGA3DRASTOUT_PSIZE );
      break;
   case TGSI_SEMANTIC_FOG:
      assert(semantic.Index == 0);
      emit->output_map[idx] = dst_register( SVGA3DREG_TEXCRDOUT, 0 );
      break;
   case TGSI_SEMANTIC_COLOR:
      /* oD0 */
      emit->output_map[idx] = dst_register( SVGA3DREG_ATTROUT,
                                            semantic.Index );
      break;
   case TGSI_SEMANTIC_GENERIC:
      emit->output_map[idx] = dst_register( SVGA3DREG_TEXCRDOUT,
                                            semantic.Index + 1 );
      break;
   default:
      assert(0);
      emit->output_map[idx] = dst_register(  SVGA3DREG_TEMP, 0 );
      return FALSE;
   }

   return TRUE;
}

static boolean ps20_sampler( struct svga_shader_emitter *emit,
                          struct tgsi_declaration_semantic semantic,
                          unsigned idx )
{
   SVGA3DOpDclArgs dcl;
   SVGA3dShaderInstToken opcode;

   opcode = inst_token( SVGA3DOP_DCL );
   dcl.values[0] = 0;
   dcl.values[1] = 0;

   dcl.dst = dst_register( SVGA3DREG_SAMPLER, idx );
   dcl.type = svga_tgsi_sampler_type( emit, idx );

   return  (emit_instruction(emit, opcode) &&
            svga_shader_emit_dwords( emit, dcl.values, Elements(dcl.values)));
}


boolean svga_translate_decl_sm20( struct svga_shader_emitter *emit,
                             const struct tgsi_full_declaration *decl )
{
   unsigned first = decl->Range.First;
   unsigned last = decl->Range.Last;
   unsigned semantic = 0;
   unsigned semantic_idx = 0;
   unsigned idx;
   
   if (decl->Declaration.Semantic) {
      semantic = decl->Semantic.Name;
      semantic_idx = decl->Semantic.Index;
   }

   for( idx = first; idx <= last; idx++ ) {
      boolean ok;

      switch (decl->Declaration.File) {
      case TGSI_FILE_SAMPLER:
         assert (emit->unit == PIPE_SHADER_FRAGMENT);
         ok = ps20_sampler( emit, decl->Semantic, idx );
         break;

      case TGSI_FILE_INPUT:
         if (emit->unit == PIPE_SHADER_VERTEX)
            ok = vs20_input( emit, decl->Semantic, idx );
         else
            ok = ps20_input( emit, decl->Semantic, idx );
         break;

      case TGSI_FILE_OUTPUT:
         if (emit->unit == PIPE_SHADER_VERTEX)
            ok = vs20_output( emit, decl->Semantic, idx );
         else
            ok = ps20_output( emit, decl->Semantic, idx );
         break;

      default:
         /* don't need to declare other vars */
         ok = TRUE;
      }

      if (!ok)
         return FALSE;
   }

   return TRUE;
}