/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8   -*-â
âvi: set et ft=c ts=4 sw=4 fenc=utf-8                                       :viâ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¡
â  PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer           â
â  Dominic Szablewski - https://phoboslab.org                                  â
â                                                                              â
â  The MIT License(MIT)                                                        â
â  Copyright(c) 2019 Dominic Szablewski                                        â
â                                                                              â
â  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 "dsp/mpeg/mpeg.h"
#include "dsp/mpeg/video.h"
#include "libc/log/check.h"

forceinline void plm_video_process_macroblock(plm_video_t *self,
                                              uint8_t *restrict d,
                                              uint8_t *restrict s, int motion_h,
                                              int motion_v, bool interpolate,
                                              unsigned BW) {
  unsigned si, di, max_address;
  int y, x, dest_scan, source_scan, dw, hp, vp, odd_h, odd_v;
  dw = self->mb_width * BW;
  hp = motion_h >> 1;
  vp = motion_v >> 1;
  odd_h = (motion_h & 1) == 1;
  odd_v = (motion_v & 1) == 1;
  si = ((self->mb_row * BW) + vp) * dw + (self->mb_col * BW) + hp;
  di = (self->mb_row * dw + self->mb_col) * BW;
  max_address = (dw * (self->mb_height * BW - BW + 1) - BW);
  if (si > max_address || di > max_address) return;
  d += di;
  s += si;
  switch (((interpolate << 2) | (odd_h << 1) | (odd_v)) & 7) {
    case 0:
      dest_scan = dw - BW;
      source_scan = dw - BW;
      for (y = 0; y < BW; y++) {
        for (x = 0; x < BW; x++) {
          *d++ = *s++;
        }
        s += source_scan;
        d += dest_scan;
      }
      break;
    case 1:
      dest_scan = dw - BW;
      source_scan = dw - BW;
      for (y = 0; y < BW; y++) {
        for (x = 0; x < BW; x++) {
          *d++ = (s[0] + s[dw] + 1) >> 1;
          s++;
        }
        s += source_scan;
        d += dest_scan;
      }
      break;
    case 2:
      dest_scan = dw - BW;
      source_scan = dw - BW;
      for (y = 0; y < BW; y++) {
        for (x = 0; x < BW; x++) {
          *d++ = (s[0] + s[1] + 1) >> 1;
          s++;
        }
        s += source_scan;
        d += dest_scan;
      }
      break;
    case 3:
      dest_scan = dw - BW;
      source_scan = dw - BW;
      for (y = 0; y < BW; y++) {
        for (x = 0; x < BW; x++) {
          *d++ = (s[0] + s[1] + s[dw] + s[dw + 1] + 2) >> 2;
          s++;
        }
        s += source_scan;
        d += dest_scan;
      }
      break;
    case 4:
      dest_scan = dw - BW;
      source_scan = dw - BW;
      for (y = 0; y < BW; y++) {
        for (x = 0; x < BW; x++) {
          d[0] = (d[0] + (s[0]) + 1) >> 1;
          d++;
          s++;
        }
        s += source_scan;
        d += dest_scan;
      }
      break;
    case 5:
      dest_scan = dw - BW;
      source_scan = dw - BW;
      for (y = 0; y < BW; y++) {
        for (x = 0; x < BW; x++) {
          d[0] = (d[0] + ((s[0] + s[dw] + 1) >> 1) + 1) >> 1;
          d++;
          s++;
        }
        s += source_scan;
        d += dest_scan;
      }
      break;
    case 6:
      dest_scan = dw - BW;
      source_scan = dw - BW;
      for (y = 0; y < BW; y++) {
        for (x = 0; x < BW; x++) {
          d[0] = (d[0] + ((s[0] + s[1] + 1) >> 1) + 1) >> 1;
          d++;
          s++;
        }
        s += source_scan;
        d += dest_scan;
      }
      break;
    case 7:
      dest_scan = dw - BW;
      source_scan = dw - BW;
      for (y = 0; y < BW; y++) {
        for (x = 0; x < BW; x++) {
          d[0] = (d[0] + ((s[0] + s[1] + s[dw] + s[dw + 1] + 2) >> 2) + 1) >> 1;
          d++;
          s++;
        }
        s += source_scan;
        d += dest_scan;
      }
      break;
    default:
      break;
  }
}

void plm_video_process_macroblock_8(plm_video_t *self, uint8_t *restrict d,
                                    uint8_t *restrict s, int motion_h,
                                    int motion_v, bool interpolate) {
  DCHECK_ALIGNED(8, d);
  DCHECK_ALIGNED(8, s);
  plm_video_process_macroblock(self, d, s, motion_h, motion_v, interpolate, 8);
}

void plm_video_process_macroblock_16(plm_video_t *self, uint8_t *restrict d,
                                     uint8_t *restrict s, int motion_h,
                                     int motion_v, bool interpolate) {
  DCHECK_ALIGNED(16, d);
  DCHECK_ALIGNED(16, s);
  plm_video_process_macroblock(self, d, s, motion_h, motion_v, interpolate, 16);
}
