#include "dirac_parse.h"
#include <string.h>

#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))

typedef struct _Unpack Unpack;

struct _Unpack
{
  unsigned char *data;
  int n_bits_left;
  int index;
  int guard_bit;
};

static void schro_unpack_init_with_data (Unpack * unpack, unsigned char *data,
    int n_bytes, unsigned int guard_bit);

static unsigned int schro_unpack_decode_bit (Unpack * unpack);
static unsigned int schro_unpack_decode_uint (Unpack * unpack);


static void schro_video_format_set_std_video_format (DiracSequenceHeader *
    format, int index);
static void schro_video_format_set_std_frame_rate (DiracSequenceHeader * format,
    int index);
static void schro_video_format_set_std_aspect_ratio (DiracSequenceHeader *
    format, int index);
static void schro_video_format_set_std_signal_range (DiracSequenceHeader *
    format, int index);
static void schro_video_format_set_std_colour_spec (DiracSequenceHeader *
    format, int index);




int
gst_dirac_sequence_header_parse (DiracSequenceHeader * header,
    unsigned char *data, int n_bytes)
{
  int bit;
  int index;
  Unpack _unpack;
  Unpack *unpack = &_unpack;
  int major_version;
  int minor_version;
  int profile;
  int level;

  memset (header, 0, sizeof (*header));

  schro_unpack_init_with_data (unpack, data, n_bytes, 1);

  /* parse parameters */
  major_version = schro_unpack_decode_uint (unpack);
  minor_version = schro_unpack_decode_uint (unpack);
  profile = schro_unpack_decode_uint (unpack);
  level = schro_unpack_decode_uint (unpack);

  /* base video header */
  index = schro_unpack_decode_uint (unpack);
  schro_video_format_set_std_video_format (header, index);

  header->major_version = major_version;
  header->minor_version = minor_version;
  header->profile = profile;
  header->level = level;

  /* source parameters */
  /* frame dimensions */
  bit = schro_unpack_decode_bit (unpack);
  if (bit) {
    header->width = schro_unpack_decode_uint (unpack);
    header->height = schro_unpack_decode_uint (unpack);
  }

  /* chroma header */
  bit = schro_unpack_decode_bit (unpack);
  if (bit) {
    header->chroma_format = schro_unpack_decode_uint (unpack);
  }

  /* scan header */
  bit = schro_unpack_decode_bit (unpack);
  if (bit) {
    header->interlaced = schro_unpack_decode_uint (unpack);
  }

  /* frame rate */
  bit = schro_unpack_decode_bit (unpack);
  if (bit) {
    index = schro_unpack_decode_uint (unpack);
    if (index == 0) {
      header->frame_rate_numerator = schro_unpack_decode_uint (unpack);
      header->frame_rate_denominator = schro_unpack_decode_uint (unpack);
    } else {
      schro_video_format_set_std_frame_rate (header, index);
    }
  }

  /* aspect ratio */
  bit = schro_unpack_decode_bit (unpack);
  if (bit) {
    index = schro_unpack_decode_uint (unpack);
    if (index == 0) {
      header->aspect_ratio_numerator = schro_unpack_decode_uint (unpack);
      header->aspect_ratio_denominator = schro_unpack_decode_uint (unpack);
    } else {
      schro_video_format_set_std_aspect_ratio (header, index);
    }
  }

  /* clean area */
  bit = schro_unpack_decode_bit (unpack);
  if (bit) {
    header->clean_width = schro_unpack_decode_uint (unpack);
    header->clean_height = schro_unpack_decode_uint (unpack);
    header->left_offset = schro_unpack_decode_uint (unpack);
    header->top_offset = schro_unpack_decode_uint (unpack);
  }

  /* signal range */
  bit = schro_unpack_decode_bit (unpack);
  if (bit) {
    index = schro_unpack_decode_uint (unpack);
    if (index == 0) {
      header->luma_offset = schro_unpack_decode_uint (unpack);
      header->luma_excursion = schro_unpack_decode_uint (unpack);
      header->chroma_offset = schro_unpack_decode_uint (unpack);
      header->chroma_excursion = schro_unpack_decode_uint (unpack);
    } else {
      schro_video_format_set_std_signal_range (header, index);
    }
  }

  /* colour spec */
  bit = schro_unpack_decode_bit (unpack);
  if (bit) {
    index = schro_unpack_decode_uint (unpack);
    schro_video_format_set_std_colour_spec (header, index);
    if (index == 0) {
      /* colour primaries */
      bit = schro_unpack_decode_bit (unpack);
      if (bit) {
        header->colour_primaries = schro_unpack_decode_uint (unpack);
      }
      /* colour matrix */
      bit = schro_unpack_decode_bit (unpack);
      if (bit) {
        header->colour_matrix = schro_unpack_decode_uint (unpack);
      }
      /* transfer function */
      bit = schro_unpack_decode_bit (unpack);
      if (bit) {
        header->transfer_function = schro_unpack_decode_uint (unpack);
      }
    }
  }

  header->interlaced_coding = schro_unpack_decode_uint (unpack);

  return 1;
}

/* standard stuff */

static const DiracSequenceHeader schro_video_formats[] = {
  {0, 0, 0, 0,
        0,                      /* custom */
        640, 480, SCHRO_CHROMA_420,
        FALSE, FALSE,
        24000, 1001, 1, 1,
        640, 480, 0, 0,
        0, 255, 128, 255,
      0, 0, 0},
  {0, 0, 0, 0,
        1,                      /* QSIF525 */
        176, 120, SCHRO_CHROMA_420,
        FALSE, FALSE,
        15000, 1001, 10, 11,
        176, 120, 0, 0,
        0, 255, 128, 255,
      1, 1, 0},
  {0, 0, 0, 0,
        2,                      /* QCIF */
        176, 144, SCHRO_CHROMA_420,
        FALSE, TRUE,
        25, 2, 12, 11,
        176, 144, 0, 0,
        0, 255, 128, 255,
      2, 1, 0},
  {0, 0, 0, 0,
        3,                      /* SIF525 */
        352, 240, SCHRO_CHROMA_420,
        FALSE, FALSE,
        15000, 1001, 10, 11,
        352, 240, 0, 0,
        0, 255, 128, 255,
      1, 1, 0},
  {0, 0, 0, 0,
        4,                      /* CIF */
        352, 288, SCHRO_CHROMA_420,
        FALSE, TRUE,
        25, 2, 12, 11,
        352, 288, 0, 0,
        0, 255, 128, 255,
      2, 1, 0},
  {0, 0, 0, 0,
        5,                      /* 4SIF525 */
        704, 480, SCHRO_CHROMA_420,
        FALSE, FALSE,
        15000, 1001, 10, 11,
        704, 480, 0, 0,
        0, 255, 128, 255,
      1, 1, 0},
  {0, 0, 0, 0,
        6,                      /* 4CIF */
        704, 576, SCHRO_CHROMA_420,
        FALSE, TRUE,
        25, 2, 12, 11,
        704, 576, 0, 0,
        0, 255, 128, 255,
      2, 1, 0},
  {0, 0, 0, 0,
        7,                      /* SD480I-60 */
        720, 480, SCHRO_CHROMA_422,
        TRUE, FALSE,
        30000, 1001, 10, 11,
        704, 480, 8, 0,
        64, 876, 512, 896,
      1, 1, 0},
  {0, 0, 0, 0,
        8,                      /* SD576I-50 */
        720, 576, SCHRO_CHROMA_422,
        TRUE, TRUE,
        25, 1, 12, 11,
        704, 576, 8, 0,
        64, 876, 512, 896,
      2, 1, 0},
  {0, 0, 0, 0,
        9,                      /* HD720P-60 */
        1280, 720, SCHRO_CHROMA_422,
        FALSE, TRUE,
        60000, 1001, 1, 1,
        1280, 720, 0, 0,
        64, 876, 512, 896,
      0, 0, 0},
  {0, 0, 0, 0,
        10,                     /* HD720P-50 */
        1280, 720, SCHRO_CHROMA_422,
        FALSE, TRUE,
        50, 1, 1, 1,
        1280, 720, 0, 0,
        64, 876, 512, 896,
      0, 0, 0},
  {0, 0, 0, 0,
        11,                     /* HD1080I-60 */
        1920, 1080, SCHRO_CHROMA_422,
        TRUE, TRUE,
        30000, 1001, 1, 1,
        1920, 1080, 0, 0,
        64, 876, 512, 896,
      0, 0, 0},
  {0, 0, 0, 0,
        12,                     /* HD1080I-50 */
        1920, 1080, SCHRO_CHROMA_422,
        TRUE, TRUE,
        25, 1, 1, 1,
        1920, 1080, 0, 0,
        64, 876, 512, 896,
      0, 0, 0},
  {0, 0, 0, 0,
        13,                     /* HD1080P-60 */
        1920, 1080, SCHRO_CHROMA_422,
        FALSE, TRUE,
        60000, 1001, 1, 1,
        1920, 1080, 0, 0,
        64, 876, 512, 896,
      0, 0, 0},
  {0, 0, 0, 0,
        14,                     /* HD1080P-50 */
        1920, 1080, SCHRO_CHROMA_422,
        FALSE, TRUE,
        50, 1, 1, 1,
        1920, 1080, 0, 0,
        64, 876, 512, 896,
      0, 0, 0},
  {0, 0, 0, 0,
        15,                     /* DC2K */
        2048, 1080, SCHRO_CHROMA_444,
        FALSE, TRUE,
        24, 1, 1, 1,
        2048, 1080, 0, 0,
        256, 3504, 2048, 3584,
      3, 0, 0},
  {0, 0, 0, 0,
        16,                     /* DC4K */
        4096, 2160, SCHRO_CHROMA_444,
        FALSE, TRUE,
        24, 1, 1, 1,
        2048, 1536, 0, 0,
        256, 3504, 2048, 3584,
      3, 0, 0},
};

static void
schro_video_format_set_std_video_format (DiracSequenceHeader * format,
    int index)
{

  if (index < 0 || index >= ARRAY_SIZE (schro_video_formats)) {
    return;
  }

  memcpy (format, schro_video_formats + index, sizeof (DiracSequenceHeader));
}

typedef struct _SchroFrameRate SchroFrameRate;
struct _SchroFrameRate
{
  int numerator;
  int denominator;
};

static const SchroFrameRate schro_frame_rates[] = {
  {0, 0},
  {24000, 1001},
  {24, 1},
  {25, 1},
  {30000, 1001},
  {30, 1},
  {50, 1},
  {60000, 1001},
  {60, 1},
  {15000, 1001},
  {25, 2}
};

static void
schro_video_format_set_std_frame_rate (DiracSequenceHeader * format, int index)
{
  if (index < 1 || index >= ARRAY_SIZE (schro_frame_rates)) {
    return;
  }

  format->frame_rate_numerator = schro_frame_rates[index].numerator;
  format->frame_rate_denominator = schro_frame_rates[index].denominator;
}

typedef struct _SchroPixelAspectRatio SchroPixelAspectRatio;
struct _SchroPixelAspectRatio
{
  int numerator;
  int denominator;
};

static const SchroPixelAspectRatio schro_aspect_ratios[] = {
  {0, 0},
  {1, 1},
  {10, 11},
  {12, 11},
  {40, 33},
  {16, 11},
  {4, 3}
};

static void
schro_video_format_set_std_aspect_ratio (DiracSequenceHeader * format,
    int index)
{
  if (index < 1 || index >= ARRAY_SIZE (schro_aspect_ratios)) {
    return;
  }

  format->aspect_ratio_numerator = schro_aspect_ratios[index].numerator;
  format->aspect_ratio_denominator = schro_aspect_ratios[index].denominator;

}

typedef struct _SchroSignalRangeStruct SchroSignalRangeStruct;
struct _SchroSignalRangeStruct
{
  int luma_offset;
  int luma_excursion;
  int chroma_offset;
  int chroma_excursion;
};

static const SchroSignalRangeStruct schro_signal_ranges[] = {
  {0, 0, 0, 0},
  {0, 255, 128, 255},
  {16, 219, 128, 224},
  {64, 876, 512, 896},
  {256, 3504, 2048, 3584}
};

static void
schro_video_format_set_std_signal_range (DiracSequenceHeader * format, int i)
{
  if (i < 1 || i >= ARRAY_SIZE (schro_signal_ranges)) {
    return;
  }

  format->luma_offset = schro_signal_ranges[i].luma_offset;
  format->luma_excursion = schro_signal_ranges[i].luma_excursion;
  format->chroma_offset = schro_signal_ranges[i].chroma_offset;
  format->chroma_excursion = schro_signal_ranges[i].chroma_excursion;
}

typedef struct _SchroColourSpecStruct SchroColourSpecStruct;
struct _SchroColourSpecStruct
{
  int colour_primaries;
  int colour_matrix;
  int transfer_function;
};

static const SchroColourSpecStruct schro_colour_specs[] = {
  {                             /* Custom */
        SCHRO_COLOUR_PRIMARY_HDTV,
        SCHRO_COLOUR_MATRIX_HDTV,
      SCHRO_TRANSFER_CHAR_TV_GAMMA},
  {                             /* SDTV 525 */
        SCHRO_COLOUR_PRIMARY_SDTV_525,
        SCHRO_COLOUR_MATRIX_SDTV,
      SCHRO_TRANSFER_CHAR_TV_GAMMA},
  {                             /* SDTV 625 */
        SCHRO_COLOUR_PRIMARY_SDTV_625,
        SCHRO_COLOUR_MATRIX_SDTV,
      SCHRO_TRANSFER_CHAR_TV_GAMMA},
  {                             /* HDTV */
        SCHRO_COLOUR_PRIMARY_HDTV,
        SCHRO_COLOUR_MATRIX_HDTV,
      SCHRO_TRANSFER_CHAR_TV_GAMMA},
  {                             /* Cinema */
        SCHRO_COLOUR_PRIMARY_CINEMA,
        SCHRO_COLOUR_MATRIX_HDTV,
      SCHRO_TRANSFER_CHAR_TV_GAMMA}
};

static void
schro_video_format_set_std_colour_spec (DiracSequenceHeader * format, int i)
{
  if (i < 0 || i >= ARRAY_SIZE (schro_colour_specs)) {
    return;
  }

  format->colour_primaries = schro_colour_specs[i].colour_primaries;
  format->colour_matrix = schro_colour_specs[i].colour_matrix;
  format->transfer_function = schro_colour_specs[i].transfer_function;
}


/* unpack */

static void
schro_unpack_init_with_data (Unpack * unpack, unsigned char *data,
    int n_bytes, unsigned int guard_bit)
{
  memset (unpack, 0, sizeof (Unpack));

  unpack->data = data;
  unpack->n_bits_left = 8 * n_bytes;
  unpack->guard_bit = guard_bit;
}

static unsigned int
schro_unpack_decode_bit (Unpack * unpack)
{
  int bit;

  if (unpack->n_bits_left < 1) {
    return unpack->guard_bit;
  }
  bit = (unpack->data[unpack->index >> 3] >> (7 - (unpack->index & 7))) & 1;
  unpack->index++;
  unpack->n_bits_left--;

  return bit;
}

static unsigned int
schro_unpack_decode_uint (Unpack * unpack)
{
  int count;
  int value;

  count = 0;
  value = 0;
  while (!schro_unpack_decode_bit (unpack)) {
    count++;
    value <<= 1;
    value |= schro_unpack_decode_bit (unpack);
  }

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