/*
  dirac.c
*/

#include "config.h"

#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif

#include "dirac.h"


typedef struct
dirac_bs_s
{
  uint8_t *p_start;
  uint8_t *p;
  uint8_t *p_end;

  int     i_left;    /* i_count number of available bits */
} dirac_bs_t;

static inline void
dirac_bs_init( dirac_bs_t *s, void *p_data, int i_data )
{
  s->p_start = p_data;
  s->p       = p_data;
  s->p_end   = s->p + i_data;
  s->i_left  = 8;
}

static inline ogg_uint32_t
dirac_bs_read( dirac_bs_t *s, int i_count )
{
   static ogg_uint32_t i_mask[33] =
   {  0x00,
      0x01,      0x03,      0x07,      0x0f,
      0x1f,      0x3f,      0x7f,      0xff,
      0x1ff,     0x3ff,     0x7ff,     0xfff,
      0x1fff,    0x3fff,    0x7fff,    0xffff,
      0x1ffff,   0x3ffff,   0x7ffff,   0xfffff,
      0x1fffff,  0x3fffff,  0x7fffff,  0xffffff,
      0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff,
      0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff};
  int      i_shr;
  ogg_uint32_t i_result = 0;

  while( i_count > 0 )
  {
    if( s->p >= s->p_end )
    {
      break;
    }

    if( ( i_shr = s->i_left - i_count ) >= 0 )
    {
      /* more in the buffer than requested */
      i_result |= ( *s->p >> i_shr )&i_mask[i_count];
      s->i_left -= i_count;
      if( s->i_left == 0 )
      {
        s->p++;
        s->i_left = 8;
      }
      return( i_result );
    }
    else
    {
        /* less in the buffer than requested */
       i_result |= (*s->p&i_mask[s->i_left]) << -i_shr;
       i_count  -= s->i_left;
       s->p++;
       s->i_left = 8;
    }
  }

  return( i_result );
}

static inline void
dirac_bs_skip( dirac_bs_t *s, int i_count )
{
  s->i_left -= i_count;

  while( s->i_left <= 0 )
  {
    s->p++;
    s->i_left += 8;
  }
}

static ogg_uint32_t
dirac_uint ( dirac_bs_t *p_bs )
{
  ogg_uint32_t count = 0, value = 0;
  while( !dirac_bs_read ( p_bs, 1 ) ) {
    count++;
    value <<= 1;
    value |= dirac_bs_read ( p_bs, 1 );
  }

  return (1<<count) - 1 + value;
}

static int
dirac_bool ( dirac_bs_t *p_bs )
{
  return dirac_bs_read ( p_bs, 1 );
}

int
dirac_parse_info (dirac_info *info, unsigned char * data, long len)
{
  dirac_bs_t bs;
  ogg_uint32_t video_format;

  static const struct {
    ogg_uint32_t fps_numerator, fps_denominator;
  } dirac_frate_tbl[] = { /* table 10.3 */
    {1,1}, /* this first value is never used */
    {24000,1001}, {24,1}, {25,1}, {30000,1001}, {30,1},
    {50,1}, {60000,1001}, {60,1}, {15000,1001}, {25,2}
  };

  static const ogg_uint32_t dirac_vidfmt_frate[] = { /* table C.1 */
    1, 9, 10, 9, 10, 9, 10, 4, 3, 7, 6, 4, 3, 7, 6, 2, 2, 7, 6, 7, 6
  };

  static const int dirac_source_sampling[] = { /* extracted from table C.1 */
    0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
  };
  static const int dirac_top_field_first[] = { /* from table C.1 */
    0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
  };

  static const struct {
    ogg_uint32_t width, height;
  } dirac_fsize_tbl[] = { /* table 10.3 framesize */
    {640,460}, {24,1}, {176,120}, {352,240}, {352,288},
    {704,480}, {704,576}, {720,480}, {720,576},
    {1280, 720}, {1280, 720}, {1920, 1080}, {1920, 1080},
    {1920, 1080}, {1920, 1080}, {2048, 1080}, {4096, 2160}
  };

  /* read in useful bits from sequence header */
  dirac_bs_init( &bs, data, len);
  dirac_bs_skip( &bs, 13*8); /* parse_info_header */
  info->major_version = dirac_uint( &bs ); /* major_version */
  info->minor_version = dirac_uint( &bs ); /* minor_version */
  info->profile = dirac_uint( &bs ); /* profile */
  info->level = dirac_uint( &bs ); /* level */
  info->video_format = video_format = dirac_uint( &bs ); /* index */

  if (video_format >= (sizeof(dirac_fsize_tbl) / sizeof(dirac_fsize_tbl[0]))) {
    return -1; 
  }

  info->width = dirac_fsize_tbl[video_format].width;
  info->height = dirac_fsize_tbl[video_format].height;
  if (dirac_bool( &bs )) {
    info->width = dirac_uint( &bs ); /* frame_width */
    info->height = dirac_uint( &bs ); /* frame_height */
  }

  if (dirac_bool( &bs )) {
    info->chroma_format = dirac_uint( &bs ); /* chroma_format */
  }

  if (dirac_bool( &bs )) { /* custom_scan_format_flag */
    int scan_format = dirac_uint( &bs ); /* scan_format */
    if (scan_format < 2) {
      info->interlaced = scan_format;
    } else { /* other scan_format values are reserved */
      info->interlaced = 0;
    }
  } else { /* no custom scan_format, use the preset value */
    info->interlaced = dirac_source_sampling[video_format];
  }
  /* field order is set by video_format and cannot be custom */
  info->top_field_first = dirac_top_field_first[video_format];

  info->fps_numerator = dirac_frate_tbl[dirac_vidfmt_frate[video_format]].fps_numerator;
  info->fps_denominator = dirac_frate_tbl[dirac_vidfmt_frate[video_format]].fps_denominator;
  if (dirac_bool( &bs )) {
    ogg_uint32_t frame_rate_index = dirac_uint( &bs );
    info->fps_numerator = dirac_frate_tbl[frame_rate_index].fps_numerator;
    info->fps_denominator = dirac_frate_tbl[frame_rate_index].fps_denominator;
    if (frame_rate_index == 0) {
      info->fps_numerator = dirac_uint( &bs );
      info->fps_denominator = dirac_uint( &bs );
    }
  }

  return 0;
}
