/**************************************************************************
 *
 * Copyright 2018 Advanced Micro Devices, 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 THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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/vl_vlc.h"
#include "va_private.h"

#define NUM_VP9_REFS 8

void vlVaHandlePictureParameterBufferVP9(vlVaDriver *drv, vlVaContext *context, vlVaBuffer *buf)
{
   VADecPictureParameterBufferVP9 *vp9 = buf->data;
   int i;

   assert(buf->size >= sizeof(VADecPictureParameterBufferVP9) && buf->num_elements == 1);

   context->desc.vp9.picture_parameter.frame_width = vp9->frame_width;
   context->desc.vp9.picture_parameter.frame_height = vp9->frame_height;

   context->desc.vp9.picture_parameter.pic_fields.subsampling_x = vp9->pic_fields.bits.subsampling_x;
   context->desc.vp9.picture_parameter.pic_fields.subsampling_y = vp9->pic_fields.bits.subsampling_y;
   context->desc.vp9.picture_parameter.pic_fields.frame_type = vp9->pic_fields.bits.frame_type;
   context->desc.vp9.picture_parameter.pic_fields.show_frame = vp9->pic_fields.bits.show_frame;
   context->desc.vp9.picture_parameter.pic_fields.error_resilient_mode = vp9->pic_fields.bits.error_resilient_mode;
   context->desc.vp9.picture_parameter.pic_fields.intra_only = vp9->pic_fields.bits.intra_only;
   context->desc.vp9.picture_parameter.pic_fields.allow_high_precision_mv = vp9->pic_fields.bits.allow_high_precision_mv;
   context->desc.vp9.picture_parameter.pic_fields.mcomp_filter_type = vp9->pic_fields.bits.mcomp_filter_type;
   context->desc.vp9.picture_parameter.pic_fields.frame_parallel_decoding_mode = vp9->pic_fields.bits.frame_parallel_decoding_mode;
   context->desc.vp9.picture_parameter.pic_fields.reset_frame_context = vp9->pic_fields.bits.reset_frame_context;
   context->desc.vp9.picture_parameter.pic_fields.refresh_frame_context = vp9->pic_fields.bits.refresh_frame_context;
   context->desc.vp9.picture_parameter.pic_fields.frame_context_idx = vp9->pic_fields.bits.frame_context_idx;
   context->desc.vp9.picture_parameter.pic_fields.segmentation_enabled = vp9->pic_fields.bits.segmentation_enabled;
   context->desc.vp9.picture_parameter.pic_fields.segmentation_temporal_update = vp9->pic_fields.bits.segmentation_temporal_update;
   context->desc.vp9.picture_parameter.pic_fields.segmentation_update_map = vp9->pic_fields.bits.segmentation_update_map;
   context->desc.vp9.picture_parameter.pic_fields.last_ref_frame = vp9->pic_fields.bits.last_ref_frame;
   context->desc.vp9.picture_parameter.pic_fields.last_ref_frame_sign_bias = vp9->pic_fields.bits.last_ref_frame_sign_bias;
   context->desc.vp9.picture_parameter.pic_fields.golden_ref_frame = vp9->pic_fields.bits.golden_ref_frame;
   context->desc.vp9.picture_parameter.pic_fields.golden_ref_frame_sign_bias = vp9->pic_fields.bits.golden_ref_frame_sign_bias;
   context->desc.vp9.picture_parameter.pic_fields.alt_ref_frame = vp9->pic_fields.bits.alt_ref_frame;
   context->desc.vp9.picture_parameter.pic_fields.alt_ref_frame_sign_bias = vp9->pic_fields.bits.alt_ref_frame_sign_bias;
   context->desc.vp9.picture_parameter.pic_fields.lossless_flag = vp9->pic_fields.bits.lossless_flag;

   context->desc.vp9.picture_parameter.filter_level = vp9->filter_level;
   context->desc.vp9.picture_parameter.sharpness_level = vp9->sharpness_level;

   context->desc.vp9.picture_parameter.log2_tile_rows = vp9->log2_tile_rows;
   context->desc.vp9.picture_parameter.log2_tile_columns = vp9->log2_tile_columns;

   context->desc.vp9.picture_parameter.frame_header_length_in_bytes = vp9->frame_header_length_in_bytes;
   context->desc.vp9.picture_parameter.first_partition_size = vp9->first_partition_size;

   for (i = 0; i < 7; ++i)
      context->desc.vp9.picture_parameter.mb_segment_tree_probs[i] = vp9->mb_segment_tree_probs[i];
   for (i = 0; i < 3; ++i)
      context->desc.vp9.picture_parameter.segment_pred_probs[i] = vp9->segment_pred_probs[i];

   context->desc.vp9.picture_parameter.profile = vp9->profile;

   context->desc.vp9.picture_parameter.bit_depth = vp9->bit_depth;

   for (i = 0 ; i < NUM_VP9_REFS ; i++) {
      if (vp9->pic_fields.bits.frame_type == 0)
         context->desc.vp9.ref[i] = NULL;
      else
         vlVaGetReferenceFrame(drv, vp9->reference_frames[i], &context->desc.vp9.ref[i]);
   }

   if (!context->decoder && !context->templat.max_references)
      context->templat.max_references = NUM_VP9_REFS;

   context->desc.vp9.slice_parameter.slice_count = 0;
   context->desc.vp9.slice_parameter.slice_info_present = false;
   memset(context->desc.vp9.slice_parameter.slice_data_flag, 0,
      sizeof(context->desc.vp9.slice_parameter.slice_data_flag));
   memset(context->desc.vp9.slice_parameter.slice_data_offset, 0,
      sizeof(context->desc.vp9.slice_parameter.slice_data_offset));
   memset(context->desc.vp9.slice_parameter.slice_data_size, 0,
      sizeof(context->desc.vp9.slice_parameter.slice_data_size));
}

void vlVaHandleSliceParameterBufferVP9(vlVaContext *context, vlVaBuffer *buf)
{
   VASliceParameterBufferVP9 *vp9 = buf->data;
   int i;

   assert(buf->size >= sizeof(VASliceParameterBufferVP9) && buf->num_elements == 1);

   ASSERTED const size_t max_pipe_vp9_slices = ARRAY_SIZE(context->desc.vp9.slice_parameter.slice_data_offset);
   assert(context->desc.vp9.slice_parameter.slice_count < max_pipe_vp9_slices);

   context->desc.vp9.slice_parameter.slice_info_present = true;
   context->desc.vp9.slice_parameter.slice_data_size[context->desc.vp9.slice_parameter.slice_count] =
      vp9->slice_data_size;
   context->desc.vp9.slice_parameter.slice_data_offset[context->desc.vp9.slice_parameter.slice_count] =
      vp9->slice_data_offset;

   switch (vp9->slice_data_flag) {
   case VA_SLICE_DATA_FLAG_ALL:
      context->desc.vp9.slice_parameter.slice_data_flag[context->desc.vp9.slice_parameter.slice_count] =
         PIPE_SLICE_BUFFER_PLACEMENT_TYPE_WHOLE;
      break;
   case VA_SLICE_DATA_FLAG_BEGIN:
      context->desc.vp9.slice_parameter.slice_data_flag[context->desc.vp9.slice_parameter.slice_count] =
         PIPE_SLICE_BUFFER_PLACEMENT_TYPE_BEGIN;
      break;
   case VA_SLICE_DATA_FLAG_MIDDLE:
      context->desc.vp9.slice_parameter.slice_data_flag[context->desc.vp9.slice_parameter.slice_count] =
         PIPE_SLICE_BUFFER_PLACEMENT_TYPE_MIDDLE;
      break;
   case VA_SLICE_DATA_FLAG_END:
      context->desc.vp9.slice_parameter.slice_data_flag[context->desc.vp9.slice_parameter.slice_count] =
         PIPE_SLICE_BUFFER_PLACEMENT_TYPE_END;
      break;
   default:
      break;
   }

   /* assert(buf->num_elements == 1) above; */
   context->desc.vp9.slice_parameter.slice_count++;

   for (i = 0; i < 8; ++i) {
      context->desc.vp9.slice_parameter.seg_param[i].segment_flags.segment_reference_enabled =
         vp9->seg_param[i].segment_flags.fields.segment_reference_enabled;
      context->desc.vp9.slice_parameter.seg_param[i].segment_flags.segment_reference =
         vp9->seg_param[i].segment_flags.fields.segment_reference;
      context->desc.vp9.slice_parameter.seg_param[i].segment_flags.segment_reference_skipped =
         vp9->seg_param[i].segment_flags.fields.segment_reference_skipped;

      memcpy(context->desc.vp9.slice_parameter.seg_param[i].filter_level, vp9->seg_param[i].filter_level, 4 * 2);

      context->desc.vp9.slice_parameter.seg_param[i].luma_ac_quant_scale = vp9->seg_param[i].luma_ac_quant_scale;
      context->desc.vp9.slice_parameter.seg_param[i].luma_dc_quant_scale = vp9->seg_param[i].luma_dc_quant_scale;
      context->desc.vp9.slice_parameter.seg_param[i].chroma_ac_quant_scale = vp9->seg_param[i].chroma_ac_quant_scale;
      context->desc.vp9.slice_parameter.seg_param[i].chroma_dc_quant_scale = vp9->seg_param[i].chroma_dc_quant_scale;
   }
}

static unsigned vp9_u(struct vl_vlc *vlc, unsigned n)
{
   unsigned valid = vl_vlc_valid_bits(vlc);

   if (n == 0)
      return 0;

   if (valid < 32)
      vl_vlc_fillbits(vlc);

   return vl_vlc_get_uimsbf(vlc, n);
}

static signed vp9_s(struct vl_vlc *vlc, unsigned n)
{
   unsigned v;
   bool s;

   v = vp9_u(vlc, n);
   s = vp9_u(vlc, 1);

   return s ? -v : v;
}

static void bitdepth_colorspace_sampling(struct vl_vlc *vlc, unsigned profile)
{
   unsigned cs;

   if (profile == 2)
      /* bit_depth */
      vp9_u(vlc, 1);

   cs = vp9_u(vlc, 3);
   if (cs != 7)
      /* yuv_range_flag */
      vp9_u(vlc, 1);
}

static void frame_size(struct vl_vlc *vlc)
{
      /* width_minus_one */
      vp9_u(vlc, 16);
      /* height_minus_one */
      vp9_u(vlc, 16);

      /* has_scaling */
      if (vp9_u(vlc, 1)) {
         /* render_width_minus_one */
         vp9_u(vlc, 16);
         /* render_height_minus_one */
         vp9_u(vlc, 16);
      }
}

void vlVaDecoderVP9BitstreamHeader(vlVaContext *context, vlVaBuffer *buf)
{
   struct vl_vlc vlc;
   unsigned profile;
   bool frame_type, show_frame, error_resilient_mode;
   bool mode_ref_delta_enabled, mode_ref_delta_update = false;
   int i;

   vl_vlc_init(&vlc, 1, (const void * const*)&buf->data,
      (const unsigned *)&context->desc.vp9.picture_parameter.frame_header_length_in_bytes);

   /* frame_marker */
   if (vp9_u(&vlc, 2) != 0x2)
      return;

   profile = vp9_u(&vlc, 1) | vp9_u(&vlc, 1) << 1;

   if (profile == 3)
      profile += vp9_u(&vlc, 1);

   if (profile != 0 && profile != 2)
      return;

   /* show_existing_frame */
   if (vp9_u(&vlc, 1))
      return;

   frame_type = vp9_u(&vlc, 1);
   show_frame = vp9_u(&vlc, 1);
   error_resilient_mode = vp9_u(&vlc, 1);

   if (frame_type == 0) {
      /* sync_code */
      if (vp9_u(&vlc, 24) != 0x498342)
         return;

      bitdepth_colorspace_sampling(&vlc, profile);
      frame_size(&vlc);
   } else {
      bool intra_only, size_in_refs = false;

      intra_only = show_frame ? 0 : vp9_u(&vlc, 1);
      if (!error_resilient_mode)
         /* reset_frame_context */
         vp9_u(&vlc, 2);

      if (intra_only) {
         /* sync_code */
         if (vp9_u(&vlc, 24) != 0x498342)
            return;

         bitdepth_colorspace_sampling(&vlc, profile);
         /* refresh_frame_flags */
         vp9_u(&vlc, 8);
         frame_size(&vlc);
      } else {
         /* refresh_frame_flags */
         vp9_u(&vlc, 8);

         for (i = 0; i < 3; ++i) {
            /* frame refs */
            vp9_u(&vlc, 3);
            vp9_u(&vlc, 1);
         }

         for (i = 0; i < 3; ++i) {
            size_in_refs = vp9_u(&vlc, 1);
            if (size_in_refs)
               break;
         }

         if (!size_in_refs) {
            /* width/height_minus_one */
            vp9_u(&vlc, 16);
            vp9_u(&vlc, 16);
         }

         if (vp9_u(&vlc, 1)) {
            /* render_width/height_minus_one */
            vp9_u(&vlc, 16);
            vp9_u(&vlc, 16);
         }

         /* high_precision_mv */
         vp9_u(&vlc, 1);
         /* filter_switchable */
         if (!vp9_u(&vlc, 1))
            /* filter_index */
            vp9_u(&vlc, 2);
      }
   }
   if (!error_resilient_mode) {
      /* refresh_frame_context */
      vp9_u(&vlc, 1);
      /* frame_parallel_decoding_mode */
      vp9_u(&vlc, 1);
   }
   /* frame_context_index */
   vp9_u(&vlc, 2);

   /* loop filter */

   /* filter_level */
   vp9_u(&vlc, 6);
   /* sharpness_level */
   vp9_u(&vlc, 3);

   mode_ref_delta_enabled = vp9_u(&vlc, 1);
   if (mode_ref_delta_enabled) {
      mode_ref_delta_update = vp9_u(&vlc, 1);
      if (mode_ref_delta_update) {
         for (i = 0; i < 4; ++i) {
            /* update_ref_delta */
            if (vp9_u(&vlc, 1))
               /* ref_deltas */
               context->desc.vp9.picture_parameter.ref_deltas[i] = vp9_s(&vlc, 6);
         }
         for (i = 0; i < 2; ++i) {
            /* update_mode_delta */
            if (vp9_u(&vlc, 1))
               /* mode_deltas */
               context->desc.vp9.picture_parameter.mode_deltas[i] = vp9_s(&vlc, 6);
         }
      }
   }
   context->desc.vp9.picture_parameter.mode_ref_delta_enabled = mode_ref_delta_enabled;
   context->desc.vp9.picture_parameter.mode_ref_delta_update = mode_ref_delta_update;

   /* quantization */

   context->desc.vp9.picture_parameter.base_qindex = vp9_u(&vlc, 8);
   context->desc.vp9.picture_parameter.y_dc_delta_q = vp9_u(&vlc, 1) ? vp9_s(&vlc, 4) : 0;
   context->desc.vp9.picture_parameter.uv_ac_delta_q = vp9_u(&vlc, 1) ? vp9_s(&vlc, 4) : 0;
   context->desc.vp9.picture_parameter.uv_dc_delta_q = vp9_u(&vlc, 1) ? vp9_s(&vlc, 4) : 0;

   /* segmentation */

   /* enabled */
   if (!vp9_u(&vlc, 1))
      return;

   /* update_map */
   if (vp9_u(&vlc, 1)) {
      for (i = 0; i < 7; ++i) {
         /* tree_probs_set */
         if (vp9_u(&vlc, 1)) {
            /* tree_probs */
            vp9_u(&vlc, 8);
         }
      }

      /* temporal_update */
      if (vp9_u(&vlc, 1)) {
         for (i = 0; i < 3; ++i) {
            /* pred_probs_set */
            if (vp9_u(&vlc, 1))
               /* pred_probs */
               vp9_u(&vlc, 8);
         }
      }
   }

   /* update_data */
   if (vp9_u(&vlc, 1)) {
      /* abs_delta */
      context->desc.vp9.picture_parameter.abs_delta = vp9_u(&vlc, 1);
      for (i = 0; i < 8; ++i) {
         /* Use alternate quantizer */
         if ((context->desc.vp9.slice_parameter.seg_param[i].alt_quant_enabled = vp9_u(&vlc, 1)))
            context->desc.vp9.slice_parameter.seg_param[i].alt_quant = vp9_s(&vlc, 8);
         /* Use alternate loop filter value */
         if ((context->desc.vp9.slice_parameter.seg_param[i].alt_lf_enabled = vp9_u(&vlc, 1)))
            context->desc.vp9.slice_parameter.seg_param[i].alt_lf = vp9_s(&vlc, 6);
         /* Optional Segment reference frame */
         if (vp9_u(&vlc, 1))
            vp9_u(&vlc, 2);
         /* Optional Segment skip mode */
         vp9_u(&vlc, 1);
      }
   }
}
