/*!
 * \brief   Assign MB map for multiple slice(s) segment
 *
 * \param   pMbMap          overall MB map
 * \param   iCountMbNum     count number of MB
 *
 * \return  0 - successful; none 0 - failed
 */
int32_t AssignMbMapMultipleSlices (SSliceCtx* pSliceSeg, const SSliceConfig* kpMso) {
  if (NULL == pSliceSeg || SM_SINGLE_SLICE == pSliceSeg->uiSliceMode)
    return 1;

  if (SM_ROWMB_SLICE == pSliceSeg->uiSliceMode) {
    const int32_t kiMbWidth = pSliceSeg->iMbWidth;
    int32_t iSliceNum = pSliceSeg->iSliceNumInFrame, uiSliceIdx = 0;

    while (uiSliceIdx < iSliceNum) {
      const int32_t kiFirstMb = uiSliceIdx * kiMbWidth;
      pSliceSeg->pCountMbNumInSlice[uiSliceIdx] = kiMbWidth;
      pSliceSeg->pFirstMbInSlice[uiSliceIdx]    = kiFirstMb;
      WelsSetMemMultiplebytes_c(pSliceSeg->pOverallMbMap + kiFirstMb, uiSliceIdx,
                                kiMbWidth, sizeof(uint16_t));
      ++ uiSliceIdx;
    }

    return 0;
  } else if (SM_RASTER_SLICE  == pSliceSeg->uiSliceMode ||
             SM_FIXEDSLCNUM_SLICE == pSliceSeg->uiSliceMode ||
             SM_AUTO_SLICE == pSliceSeg->uiSliceMode) {
    const int32_t* kpSlicesAssignList           = (int32_t*) & (kpMso->sSliceArgument.uiSliceMbNum[0]);
    const int32_t kiCountNumMbInFrame           = pSliceSeg->iMbNumInFrame;
    const int32_t kiCountSliceNumInFrame        = pSliceSeg->iSliceNumInFrame;
    uint16_t iSliceIdx                          = 0;
    int32_t iMbIdx                              = 0;

    do {
      const int32_t kiCurRunLength      = kpSlicesAssignList[iSliceIdx];
      int32_t iRunIdx                   = 0;

      pSliceSeg->pFirstMbInSlice[iSliceIdx]     = iMbIdx;
      pSliceSeg->pCountMbNumInSlice[iSliceIdx]  = kiCurRunLength;

      // due here need check validate mb_assign_map for input pData, can not use memset
      do {
        pSliceSeg->pOverallMbMap[iMbIdx + iRunIdx] = iSliceIdx;
        ++ iRunIdx;
      } while (iRunIdx < kiCurRunLength && iMbIdx + iRunIdx < kiCountNumMbInFrame);

      iMbIdx += kiCurRunLength;
      ++ iSliceIdx;
    } while (iSliceIdx < kiCountSliceNumInFrame && iMbIdx < kiCountNumMbInFrame);
  } else if (SM_DYN_SLICE == pSliceSeg->uiSliceMode) {
    int32_t iSliceIdx = 0;
    const int32_t kiMaxSliceNum = pSliceSeg->iMaxSliceNumConstraint;
    const int32_t kiCountNumMbInFrame = pSliceSeg->iMbNumInFrame;
    do {
      pSliceSeg->pFirstMbInSlice[iSliceIdx] = 0;
      pSliceSeg->pCountMbNumInSlice[iSliceIdx] = kiCountNumMbInFrame;
      iSliceIdx++;
    } while (iSliceIdx < kiMaxSliceNum);
  } else { // any else uiSliceMode?
    assert (0);
  }

  // extention for other multiple slice type in the future
  return 1;
}
/*!
 * \brief   Assign MB map for multiple slice(s) segment
 *
 * \param   pCurDq              current layer which its MB map will be assigned
 * \param   kpSliceArgument     slice argument for current layer
 *
 * \return  0 - successful; none 0 - failed
 */
int32_t AssignMbMapMultipleSlices (SDqLayer* pCurDq,const SSliceArgument* kpSliceArgument) {
  SSliceCtx* pSliceSeg   = &pCurDq->sSliceEncCtx;
  int32_t iSliceIdx      = 0;
  if (NULL == pSliceSeg || SM_SINGLE_SLICE == pSliceSeg->uiSliceMode)
    return 1;

  if ((SM_RASTER_SLICE == pSliceSeg->uiSliceMode) && (0 == kpSliceArgument->uiSliceMbNum[0])) {
    const int32_t kiMbWidth = pSliceSeg->iMbWidth;
    int32_t iSliceNum = pSliceSeg->iSliceNumInFrame;

    iSliceIdx = 0;
    while (iSliceIdx < iSliceNum) {
      const int32_t kiFirstMb                       = iSliceIdx * kiMbWidth;
      WelsSetMemMultiplebytes_c(pSliceSeg->pOverallMbMap + kiFirstMb, iSliceIdx,
                                kiMbWidth, sizeof(uint16_t));
      ++ iSliceIdx;
    }

    return 0;
  } else if (SM_RASTER_SLICE  == pSliceSeg->uiSliceMode ||
             SM_FIXEDSLCNUM_SLICE == pSliceSeg->uiSliceMode) {
    const int32_t* kpSlicesAssignList           = (int32_t*) & (kpSliceArgument->uiSliceMbNum[0]);
    const int32_t kiCountNumMbInFrame           = pSliceSeg->iMbNumInFrame;
    const int32_t kiCountSliceNumInFrame        = pSliceSeg->iSliceNumInFrame;
    int32_t iMbIdx                              = 0;

    iSliceIdx = 0;
    do {
      const int32_t kiCurRunLength                  = kpSlicesAssignList[iSliceIdx];
      int32_t iRunIdx                               = 0;

      // due here need check validate mb_assign_map for input pData, can not use memset
      do {
        pSliceSeg->pOverallMbMap[iMbIdx + iRunIdx] = iSliceIdx;
        ++ iRunIdx;
      } while (iRunIdx < kiCurRunLength && iMbIdx + iRunIdx < kiCountNumMbInFrame);

      iMbIdx += kiCurRunLength;
      ++ iSliceIdx;
    } while (iSliceIdx < kiCountSliceNumInFrame && iMbIdx < kiCountNumMbInFrame);
  } else if (SM_SIZELIMITED_SLICE == pSliceSeg->uiSliceMode) {
     // do nothing,pSliceSeg->pOverallMbMap will be initial later
  } else { // any else uiSliceMode?
    assert (0);
  }

  // extention for other multiple slice type in the future
  return 1;
}
int32_t DynamicAdjustSlicePEncCtxAll (SSliceCtx* pSliceCtx,
                                      int32_t* pRunLength) {
  const int32_t iCountNumMbInFrame      = pSliceCtx->iMbNumInFrame;
  const int32_t iCountSliceNumInFrame   = pSliceCtx->iSliceNumInFrame;
  int32_t iSameRunLenFlag               = 1;
  int32_t iFirstMbIdx                   = 0;
  int32_t iSliceIdx                     = 0;

  assert (iCountSliceNumInFrame <= MAX_THREADS_NUM);

  while (iSliceIdx < iCountSliceNumInFrame) {
    if (pRunLength[iSliceIdx] != pSliceCtx->pCountMbNumInSlice[iSliceIdx]) {
      iSameRunLenFlag = 0;
      break;
    }
    ++ iSliceIdx;
  }
  if (iSameRunLenFlag) {
    return 1; // do not need adjust it due to same running length as before to save complexity
  }

  iSliceIdx = 0;
  do {
    const int32_t kiSliceRun = pRunLength[iSliceIdx];

    pSliceCtx->pFirstMbInSlice[iSliceIdx]    = iFirstMbIdx;
    pSliceCtx->pCountMbNumInSlice[iSliceIdx] = kiSliceRun;

    WelsSetMemMultiplebytes_c(pSliceCtx->pOverallMbMap + iFirstMbIdx, iSliceIdx,
                              kiSliceRun, sizeof(uint16_t));

    iFirstMbIdx += kiSliceRun;

    ++ iSliceIdx;
  } while (iSliceIdx < iCountSliceNumInFrame && iFirstMbIdx < iCountNumMbInFrame);

  return 0;
}
/*!
 * \brief   Initialize slice segment (Single/multiple slices)
 *
 * \param   pSliceSeg           SSlice segment to be initialized
 * \param   uiSliceMode         SSlice mode
 * \param   multi_slice_argv    Multiple slices argument
 * \param   iMbWidth            MB width
 * \param   iMbHeight           MB height
 *
 * \return  0 - successful; none 0 - failed;
 */
int32_t InitSliceSegment (SSliceCtx* pSliceSeg,
                          CMemoryAlign* pMa,
                          SSliceConfig* pMso,
                          const int32_t kiMbWidth,
                          const int32_t kiMbHeight) {
  const int32_t kiCountMbNum = kiMbWidth * kiMbHeight;
  SliceModeEnum uiSliceMode = SM_SINGLE_SLICE;

  if (NULL == pSliceSeg || NULL == pMso || kiMbWidth == 0 || kiMbHeight == 0)
    return 1;

  uiSliceMode = pMso->uiSliceMode;
  if (pSliceSeg->iMbNumInFrame == kiCountMbNum && pSliceSeg->iMbWidth == kiMbWidth
      && pSliceSeg->iMbHeight == kiMbHeight && pSliceSeg->uiSliceMode == uiSliceMode && pSliceSeg->pOverallMbMap != NULL)
    return 0;
  else if (pSliceSeg->iMbNumInFrame != kiCountMbNum) {
    if (NULL != pSliceSeg->pOverallMbMap) {
      pMa->WelsFree (pSliceSeg->pOverallMbMap, "pSliceSeg->pOverallMbMap");

      pSliceSeg->pOverallMbMap = NULL;
    }
    if (NULL != pSliceSeg->pFirstMbInSlice) {
      pMa->WelsFree (pSliceSeg->pFirstMbInSlice, "pSliceSeg->pFirstMbInSlice");

      pSliceSeg->pFirstMbInSlice = NULL;
    }
    if (NULL != pSliceSeg->pCountMbNumInSlice) {
      pMa->WelsFree (pSliceSeg->pCountMbNumInSlice, "pSliceSeg->pCountMbNumInSlice");

      pSliceSeg->pCountMbNumInSlice = NULL;
    }
    // just for safe
    pSliceSeg->iSliceNumInFrame = 0;
    pSliceSeg->iMbNumInFrame    = 0;
    pSliceSeg->iMbWidth         = 0;
    pSliceSeg->iMbHeight        = 0;
    pSliceSeg->uiSliceMode      = SM_SINGLE_SLICE;      // sigle in default
  }

  if (SM_SINGLE_SLICE == uiSliceMode) {
    pSliceSeg->pOverallMbMap = (uint16_t*)pMa->WelsMalloc (kiCountMbNum * sizeof (uint16_t), "pSliceSeg->pOverallMbMap");

    WELS_VERIFY_RETURN_IF (1, NULL == pSliceSeg->pOverallMbMap)
    pSliceSeg->iSliceNumInFrame = 1;

    pSliceSeg->pFirstMbInSlice = (int32_t*)pMa->WelsMalloc (pSliceSeg->iSliceNumInFrame * sizeof (int32_t),
                                  "pSliceSeg->pFirstMbInSlice");

    WELS_VERIFY_RETURN_IF (1, NULL == pSliceSeg->pFirstMbInSlice)

    pSliceSeg->pCountMbNumInSlice = (int32_t*)pMa->WelsMalloc (pSliceSeg->iSliceNumInFrame * sizeof (int32_t),
                                    "pSliceSeg->pCountMbNumInSlice");

    WELS_VERIFY_RETURN_IF (1, NULL == pSliceSeg->pCountMbNumInSlice)
    pSliceSeg->uiSliceMode              = uiSliceMode;
    pSliceSeg->iMbWidth                 = kiMbWidth;
    pSliceSeg->iMbHeight                = kiMbHeight;
    pSliceSeg->iMbNumInFrame            = kiCountMbNum;
    pSliceSeg->pCountMbNumInSlice[0]    = kiCountMbNum;
    pSliceSeg->pFirstMbInSlice[0]       = 0;

    return AssignMbMapSingleSlice (pSliceSeg->pOverallMbMap, kiCountMbNum, sizeof (pSliceSeg->pOverallMbMap[0]));
  } else { //if ( SM_MULTIPLE_SLICE == uiSliceMode )
    if (uiSliceMode != SM_FIXEDSLCNUM_SLICE && uiSliceMode != SM_ROWMB_SLICE && uiSliceMode != SM_RASTER_SLICE
        && uiSliceMode != SM_DYN_SLICE && uiSliceMode != SM_AUTO_SLICE)
      return 1;

    pSliceSeg->pOverallMbMap = (uint16_t*)pMa->WelsMalloc (kiCountMbNum * sizeof (uint16_t), "pSliceSeg->pOverallMbMap");

    WELS_VERIFY_RETURN_IF (1, NULL == pSliceSeg->pOverallMbMap)

    WelsSetMemMultiplebytes_c(pSliceSeg->pOverallMbMap, 0, kiCountMbNum, sizeof(uint16_t));

    //SM_DYN_SLICE: init, set pSliceSeg->iSliceNumInFrame = 1;
    pSliceSeg->iSliceNumInFrame = GetInitialSliceNum (kiMbWidth, kiMbHeight, pMso);

    if (-1 == pSliceSeg->iSliceNumInFrame)
      return 1;

    pSliceSeg->pCountMbNumInSlice = (int32_t*)pMa->WelsMalloc (pSliceSeg->iSliceNumInFrame * sizeof (int32_t),
                                    "pSliceSeg->pCountMbNumInSlice");

    WELS_VERIFY_RETURN_IF (1, NULL == pSliceSeg->pCountMbNumInSlice)

    pSliceSeg->pFirstMbInSlice = (int32_t*)pMa->WelsMalloc (pSliceSeg->iSliceNumInFrame * sizeof (int32_t),
                                    "pSliceSeg->pFirstMbInSlice");

    WELS_VERIFY_RETURN_IF (1, NULL == pSliceSeg->pFirstMbInSlice)
    pSliceSeg->uiSliceMode      = pMso->uiSliceMode;
    pSliceSeg->iMbWidth         = kiMbWidth;
    pSliceSeg->iMbHeight        = kiMbHeight;
    pSliceSeg->iMbNumInFrame    = kiCountMbNum;
    if (SM_DYN_SLICE == pMso->uiSliceMode) {
      if (0 < pMso->sSliceArgument.uiSliceSizeConstraint) {
        pSliceSeg->uiSliceSizeConstraint = pMso->sSliceArgument.uiSliceSizeConstraint;
      } else {
        return 1;
      }
    } else {
      pSliceSeg->uiSliceSizeConstraint = DEFAULT_MAXPACKETSIZE_CONSTRAINT;
    }
    // about "iMaxSliceNumConstraint"
    //only used in SM_DYN_SLICE mode so far,
    //now follows NAL_UNIT_CONSTRAINT, (see definition)
    //will be adjusted under MT if there is limitation on iLayerNum
    pSliceSeg->iMaxSliceNumConstraint = MAX_SLICES_NUM;


    return AssignMbMapMultipleSlices (pSliceSeg, pMso);
  }
  return 0;
}