/* getvlc.c, variable length decoding                                       */

/* Copyright (C) 1996, MPEG Software Simulation Group. All Rights Reserved. */

/*
 * Disclaimer of Warranty
 *
 * These software programs are available to the user without any license fee or
 * royalty on an "as is" basis.  The MPEG Software Simulation Group disclaims
 * any and all warranties, whether express, implied, or statuary, including any
 * implied warranties or merchantability or of fitness for a particular
 * purpose.  In no event shall the copyright-holder be liable for any
 * incidental, punitive, or consequential damages of any kind whatsoever
 * arising from the use of these programs.
 *
 * This disclaimer of warranty extends to the user of these programs and user's
 * customers, employees, agents, transferees, successors, and assigns.
 *
 * The MPEG Software Simulation Group does not represent or warrant that the
 * programs furnished hereunder are free of infringement of any third-party
 * patents.
 *
 * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware,
 * are subject to royalty fees to patent holders.  Many of these patents are
 * general enough such that they are unavoidable regardless of implementation
 * design.
 *
 */

#include "config.h"

#include <stdio.h>

#include "mpeg-config.h"
#include "global.h"
#include "getvlc.h"

/* private prototypes */
/* generic picture macroblock type processing functions */
static int Get_I_macroblock_type _ANSI_ARGS_((struct mpeg2obj *m));
static int Get_P_macroblock_type _ANSI_ARGS_((struct mpeg2obj *m));
static int Get_B_macroblock_type _ANSI_ARGS_((struct mpeg2obj *m));
static int Get_D_macroblock_type _ANSI_ARGS_((struct mpeg2obj *m));

/* spatial picture macroblock type processing functions */
static int Get_I_Spatial_macroblock_type _ANSI_ARGS_((struct mpeg2obj *m));
static int Get_P_Spatial_macroblock_type _ANSI_ARGS_((struct mpeg2obj *m));
static int Get_B_Spatial_macroblock_type _ANSI_ARGS_((struct mpeg2obj *m));
static int Get_SNR_macroblock_type _ANSI_ARGS_((struct mpeg2obj *m));

int _mpeg2Get_macroblock_type(struct mpeg2obj *m)
{
  int macroblock_type = 0;

  if (m->ld->scalable_mode==SC_SNR)
    macroblock_type = Get_SNR_macroblock_type(m);
  else
  {
    switch (m->picture_coding_type)
    {
    case I_TYPE:
      macroblock_type = m->ld->pict_scal ? Get_I_Spatial_macroblock_type(m) : Get_I_macroblock_type(m);
      break;
    case P_TYPE:
      macroblock_type = m->ld->pict_scal ? Get_P_Spatial_macroblock_type(m) : Get_P_macroblock_type(m);
      break;
    case B_TYPE:
      macroblock_type = m->ld->pict_scal ? Get_B_Spatial_macroblock_type(m) : Get_B_macroblock_type(m);
      break;
    case D_TYPE:
      macroblock_type = Get_D_macroblock_type(m);
      break;
    default:
      printf("Get_macroblock_type(): unrecognized picture coding type\n");
      break;
    }
  }

  return macroblock_type;
}

static int Get_I_macroblock_type(struct mpeg2obj *m)
{

  if (_mpeg2Get_Bits1(m))
  {
    return 1;
  }

  if (!_mpeg2Get_Bits1(m))
  {
    if (!_mpeg2Quiet_Flag)
      printf("Invalid macroblock_type code\n");
    m->Fault_Flag = 1;
  }

  return 17;
}

#if 0
static const char *MBdescr[]={
  "",                  "Intra",        "No MC, Coded",         "",
  "Bwd, Not Coded",    "",             "Bwd, Coded",           "",
  "Fwd, Not Coded",    "",             "Fwd, Coded",           "",
  "Interp, Not Coded", "",             "Interp, Coded",        "",
  "",                  "Intra, Quant", "No MC, Coded, Quant",  "",
  "",                  "",             "Bwd, Coded, Quant",    "",
  "",                  "",             "Fwd, Coded, Quant",    "",
  "",                  "",             "Interp, Coded, Quant", ""
};
#endif

static int Get_P_macroblock_type(struct mpeg2obj *m)
{
  int code;

  if ((code = _mpeg2Show_Bits(m,6))>=8)
  {
    code >>= 3;
    _mpeg2Flush_Buffer(m,PMBtab0[code].len);
    return PMBtab0[code].val;
  }

  if (code==0)
  {
    if (!_mpeg2Quiet_Flag)
      printf("Invalid macroblock_type code\n");
    m->Fault_Flag = 1;
    return 0;
  }

  _mpeg2Flush_Buffer(m,PMBtab1[code].len);

  return PMBtab1[code].val;
}

static int Get_B_macroblock_type(struct mpeg2obj *m)
{
  int code;

  if ((code = _mpeg2Show_Bits(m,6))>=8)
  {
    code >>= 2;
    _mpeg2Flush_Buffer(m,BMBtab0[code].len);

    return BMBtab0[code].val;
  }

  if (code==0)
  {
    if (!_mpeg2Quiet_Flag)
      printf("Invalid macroblock_type code\n");
    m->Fault_Flag = 1;
    return 0;
  }

  _mpeg2Flush_Buffer(m,BMBtab1[code].len);

  return BMBtab1[code].val;
}

static int Get_D_macroblock_type(struct mpeg2obj *m)
{
  if (!_mpeg2Get_Bits1(m))
  {
    if (!_mpeg2Quiet_Flag)
      printf("Invalid macroblock_type code\n");
    m->Fault_Flag=1;
  }

  return 1;
}

/* macroblock_type for pictures with spatial scalability */
static int Get_I_Spatial_macroblock_type(struct mpeg2obj *m)
{
  int code;

  code = _mpeg2Show_Bits(m,4);

  if (code==0)
  {
    if (!_mpeg2Quiet_Flag)
      printf("Invalid macroblock_type code\n");
    m->Fault_Flag = 1;
    return 0;
  }

  _mpeg2Flush_Buffer(m,spIMBtab[code].len);
  return spIMBtab[code].val;
}

static int Get_P_Spatial_macroblock_type(struct mpeg2obj *m)
{
  int code;

  code = _mpeg2Show_Bits(m,7);

  if (code<2)
  {
    if (!_mpeg2Quiet_Flag)
      printf("Invalid macroblock_type code\n");
    m->Fault_Flag = 1;
    return 0;
  }

  if (code>=16)
  {
    code >>= 3;
    _mpeg2Flush_Buffer(m,spPMBtab0[code].len);

    return spPMBtab0[code].val;
  }

  _mpeg2Flush_Buffer(m,spPMBtab1[code].len);

  return spPMBtab1[code].val;
}

static int Get_B_Spatial_macroblock_type(struct mpeg2obj *m)
{
  int code;
  VLCtab *p;

  code = _mpeg2Show_Bits(m,9);

  if (code>=64)
    p = &spBMBtab0[(code>>5)-2];
  else if (code>=16)
    p = &spBMBtab1[(code>>2)-4];
  else if (code>=8)
    p = &spBMBtab2[code-8];
  else
  {
    if (!_mpeg2Quiet_Flag)
      printf("Invalid macroblock_type code\n");
    m->Fault_Flag = 1;
    return 0;
  }

  _mpeg2Flush_Buffer(m,p->len);

  return p->val;
}

static int Get_SNR_macroblock_type(struct mpeg2obj *m)
{
  int code;

  code = _mpeg2Show_Bits(m,3);

  if (code==0)
  {
    if (!_mpeg2Quiet_Flag)
      printf("Invalid macroblock_type code\n");
    m->Fault_Flag = 1;
    return 0;
  }

  _mpeg2Flush_Buffer(m,SNRMBtab[code].len);

  return SNRMBtab[code].val;
}

int _mpeg2Get_motion_code(struct mpeg2obj *m)
{
  int code;

  if (_mpeg2Get_Bits1(m))
  {
    return 0;
  }

  if ((code = _mpeg2Show_Bits(m,9))>=64)
  {
    code >>= 6;
    _mpeg2Flush_Buffer(m,MVtab0[code].len);

    return _mpeg2Get_Bits1(m)?-MVtab0[code].val:MVtab0[code].val;
  }

  if (code>=24)
  {
    code >>= 3;
    _mpeg2Flush_Buffer(m,MVtab1[code].len);

    return _mpeg2Get_Bits1(m)?-MVtab1[code].val:MVtab1[code].val;
  }

  if ((code-=12)<0)
  {
#if 0
    if (!_mpeg2Quiet_Flag)
/* HACK */
      printf("Invalid motion_vector code (MBA %d, pic %d)\n", global_MBA, global_pic);
#endif
    m->Fault_Flag=1;
    return 0;
  }

  _mpeg2Flush_Buffer(m,MVtab2[code].len);

  return _mpeg2Get_Bits1(m) ? -MVtab2[code].val : MVtab2[code].val;
}

/* get differential motion vector (for dual prime prediction) */
int _mpeg2Get_dmvector(struct mpeg2obj *m)
{
  if (_mpeg2Get_Bits(m,1))
  {
    return _mpeg2Get_Bits(m,1) ? -1 : 1;
  }
  else
  {
    return 0;
  }
}

int _mpeg2Get_coded_block_pattern(struct mpeg2obj *m)
{
  int code;

  if ((code = _mpeg2Show_Bits(m,9))>=128)
  {
    code >>= 4;
    _mpeg2Flush_Buffer(m,CBPtab0[code].len);

    return CBPtab0[code].val;
  }

  if (code>=8)
  {
    code >>= 1;
    _mpeg2Flush_Buffer(m,CBPtab1[code].len);

    return CBPtab1[code].val;
  }

  if (code<1)
  {
    if (!_mpeg2Quiet_Flag)
      printf("Invalid coded_block_pattern code\n");
    m->Fault_Flag = 1;
    return 0;
  }

  _mpeg2Flush_Buffer(m,CBPtab2[code].len);

  return CBPtab2[code].val;
}

int _mpeg2Get_macroblock_address_increment(struct mpeg2obj *m)
{
  int code, val;

  val = 0;

  while ((code = _mpeg2Show_Bits(m,11))<24)
  {
    if (code!=15) /* if not macroblock_stuffing */
    {
      if (code==8) /* if macroblock_escape */
      {
        val+= 33;
      }
      else
      {
        if (!_mpeg2Quiet_Flag)
          printf("Invalid macroblock_address_increment code\n");
	
        m->Fault_Flag = 1;
        return 1;
      }
    }
    else /* macroblock suffing */
    {
#ifdef TRACE
      if (Trace_Flag)
        printf("00000001111 ");
#endif /* TRACE */
    }

    _mpeg2Flush_Buffer(m,11);
  }

  /* macroblock_address_increment == 1 */
  /* ('1' is in the MSB position of the lookahead) */
  if (code>=1024)
  {
    _mpeg2Flush_Buffer(m,1);
    return val + 1;
  }

  /* codes 00010 ... 011xx */
  if (code>=128)
  {
    /* remove leading zeros */
    code >>= 6;
    _mpeg2Flush_Buffer(m,MBAtab1[code].len);

    return val + MBAtab1[code].val;
  }
  
  /* codes 00000011000 ... 0000111xxxx */
  code-= 24; /* remove common base */
  _mpeg2Flush_Buffer(m,MBAtab2[code].len);

  return val + MBAtab2[code].val;
}

/* combined MPEG-1 and MPEG-2 stage. parse VLC and 
   perform dct_diff arithmetic.

   MPEG-1:  ISO/IEC 11172-2 section
   MPEG-2:  ISO/IEC 13818-2 section 7.2.1 
   
   Note: the arithmetic here is presented more elegantly than
   the spec, yet the results, dct_diff, are the same.
*/

int _mpeg2Get_Luma_DC_dct_diff(struct mpeg2obj *m)
{
  int code, size, dct_diff;

  /* decode length */
  code = _mpeg2Show_Bits(m,5);

  if (code<31)
  {
    size = DClumtab0[code].val;
    _mpeg2Flush_Buffer(m,DClumtab0[code].len);
  }
  else
  {
    code = _mpeg2Show_Bits(m,9) - 0x1f0;
    size = DClumtab1[code].val;
    _mpeg2Flush_Buffer(m,DClumtab1[code].len);
  }

  if (size==0)
    dct_diff = 0;
  else
  {
    dct_diff = _mpeg2Get_Bits(m,size);
    if ((dct_diff & (1<<(size-1)))==0)
      dct_diff-= (1<<size) - 1;
  }

  return dct_diff;
}


int _mpeg2Get_Chroma_DC_dct_diff(struct mpeg2obj *m)
{
  int code, size, dct_diff;

  /* decode length */
  code = _mpeg2Show_Bits(m,5);

  if (code<31)
  {
    size = DCchromtab0[code].val;
    _mpeg2Flush_Buffer(m,DCchromtab0[code].len);
  }
  else
  {
    code = _mpeg2Show_Bits(m,10) - 0x3e0;
    size = DCchromtab1[code].val;
    _mpeg2Flush_Buffer(m,DCchromtab1[code].len);
  }

  if (size==0)
    dct_diff = 0;
  else
  {
    dct_diff = _mpeg2Get_Bits(m,size);
    if ((dct_diff & (1<<(size-1)))==0)
      dct_diff-= (1<<size) - 1;
  }

  return dct_diff;
}
