/*******************************************************************************
* Copyright 2016-2020 Intel Corporation.
*
* This software and the related documents are Intel copyrighted  materials,  and
* your use of  them is  governed by the  express license  under which  they were
* provided to you (License).  Unless the License provides otherwise, you may not
* use, modify, copy, publish, distribute,  disclose or transmit this software or
* the related documents without Intel's prior written permission.
*
* This software and the related documents  are provided as  is,  with no express
* or implied  warranties,  other  than those  that are  expressly stated  in the
* License.
*******************************************************************************/

#include "iw/iw_image_op.h"
#include "iw_owni.h"

IW_DECL(IppStatus) llwiMulC(const double *pValue, const void *pSrc, int srcStep, void *pDst, int dstStep,
                              IppiSize size, IppDataType dataType, int channels, IppHintAlgorithm algoMode, IppRoundMode round, int scaleFactor, IwiChDescriptor chDesc);

/**/////////////////////////////////////////////////////////////////////////////
//                   iwiMulC
///////////////////////////////////////////////////////////////////////////// */
IW_DECL(IppStatus) iwiMulC(const double *pValue, int valuesNum, const IwiImage *pSrcImage, IwiImage *pDstImage, const IwiMulCParams *pAuxParams, const IwiTile *pTile)
{
    IppStatus     status;
    IwiMulCParams auxParams;

    if(!pValue)
        return ippStsNullPtrErr;
    status = owniCheckImageRead(pSrcImage);
    if(status)
        return status;
    status = owniCheckImageWrite(pDstImage);
    if(status)
        return status;

    if(pSrcImage->m_typeSize != pDstImage->m_typeSize ||
        pSrcImage->m_channels != pDstImage->m_channels)
        return ippStsBadArgErr;

    if(pAuxParams)
        auxParams = *pAuxParams;
    else
        iwiMulC_SetDefaultParams(&auxParams);

    {
        const void *pSrc = pSrcImage->m_ptrConst;
        void       *pDst = pDstImage->m_ptr;
        IwiSize     size = owniGetMinSize(&pSrcImage->m_size, &pDstImage->m_size);

        if(pTile && pTile->m_initialized != ownTileInitNone)
        {
            if(pTile->m_initialized == ownTileInitSimple)
            {
                IwiRoi dstRoi = pTile->m_dstRoi;

                if(!owniTile_BoundToSize(&dstRoi, &size))
                    return ippStsNoOperation;

                pSrc = iwiImage_GetPtrConst(pSrcImage, dstRoi.y, dstRoi.x, 0);
                pDst = iwiImage_GetPtr(pDstImage, dstRoi.y, dstRoi.x, 0);
            }
            else if(pTile->m_initialized == ownTileInitPipe)
            {
                IwiRoi srcLim;
                IwiRoi dstLim;
                iwiTilePipeline_GetBoundedSrcRoi(pTile, &srcLim);
                iwiTilePipeline_GetBoundedDstRoi(pTile, &dstLim);

                pSrc = iwiImage_GetPtrConst(pSrcImage, srcLim.y, srcLim.x, 0);
                pDst = iwiImage_GetPtr(pDstImage, dstLim.y, dstLim.x, 0);

                size = owniGetMinSizeFromRect(&srcLim, &dstLim);
            }
            else
                return ippStsContextMatchErr;
        }

        // Long compatibility check
        {
            IppiSize _size;

            status = ownLongCompatCheckValue(pSrcImage->m_step, NULL);
            if(status < 0)
                return status;

            status = ownLongCompatCheckValue(pDstImage->m_step, NULL);
            if(status < 0)
                return status;

            status = owniLongCompatCheckSize(size, &_size);
            if(status < 0)
                return status;

            if(valuesNum < pDstImage->m_channels)
            {
                int    i;
                Ipp64f val[4];
                for(i = 0; i < valuesNum; i++)
                    val[i] = pValue[i];
                for(i = valuesNum; i < pDstImage->m_channels; i++)
                    val[i] = pValue[valuesNum-1];

                return llwiMulC(val, pSrc, (int)pSrcImage->m_step, pDst, (int)pDstImage->m_step, _size, pSrcImage->m_dataType,
                    pSrcImage->m_channels, auxParams.algoMode, auxParams.roundMode, auxParams.scaleFactor, auxParams.chDesc);
            }

            return llwiMulC(pValue, pSrc, (int)pSrcImage->m_step, pDst, (int)pDstImage->m_step, _size, pSrcImage->m_dataType,
                pSrcImage->m_channels, auxParams.algoMode, auxParams.roundMode, auxParams.scaleFactor, auxParams.chDesc);
        }
    }
}


/**/////////////////////////////////////////////////////////////////////////////
//                   Low-Level Wrappers
///////////////////////////////////////////////////////////////////////////// */
IW_DECL(IppStatus) llwiMulC(const double *pValue, const void *pSrc, int srcStep, void *pDst, int dstStep,
                              IppiSize size, IppDataType dataType, int channels, IppHintAlgorithm algoMode, IppRoundMode round, int scaleFactor, IwiChDescriptor chDesc)
{
    OwniChCodes chCode = owniChDescriptorToCode(chDesc, channels, channels);

    int    i;
    Ipp64f value[4];
    int    intFlag = 1;
    int    intVal;
    for(i = 0; i < channels; i++)
    {
        intVal = (int)pValue[i];
        if(IPP_ABS(pValue[i]-intVal) > IPP_EPS_64F)
        {
            intFlag = 0;
            break;
        }
    }

    if(intFlag)
    {
        if(pSrc == pDst)
        {
            switch(dataType)
            {
#if IW_ENABLE_DATA_TYPE_8U
            case ipp8u:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC_8u_C1IRSfs(ownCast_64f8u(*pValue), (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC_8u_C3IRSfs(ownCastArray_64f8u(pValue, value, 3), (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC_8u_C4IRSfs(ownCastArray_64f8u(pValue, value, 4), (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
                case owniC4M1110:   return ippiMulC_8u_AC4IRSfs(ownCastArray_64f8u(pValue, value, 4), (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_16U
            case ipp16u:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC_16u_C1IRSfs(ownCast_64f16u(*pValue), (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC_16u_C3IRSfs(ownCastArray_64f16u(pValue, value, 3), (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC_16u_C4IRSfs(ownCastArray_64f16u(pValue, value, 4), (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
                case owniC4M1110:   return ippiMulC_16u_AC4IRSfs(ownCastArray_64f16u(pValue, value, 4), (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_16S
            case ipp16s:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC_16s_C1IRSfs(ownCast_64f16s(*pValue), (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC_16s_C3IRSfs(ownCastArray_64f16s(pValue, value, 3), (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC_16s_C4IRSfs(ownCastArray_64f16s(pValue, value, 4), (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
                case owniC4M1110:   return ippiMulC_16s_AC4IRSfs(ownCastArray_64f16s(pValue, value, 4), (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_32F
            case ipp32f:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC_32f_C1IR(ownCast_64f32f(*pValue), (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC_32f_C3IR(ownCastArray_64f32f(pValue, value, 3), (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC_32f_C4IR(ownCastArray_64f32f(pValue, value, 4), (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_AC4
                case owniC4M1110:   return ippiMulC_32f_AC4IR(ownCastArray_64f32f(pValue, value, 4), (Ipp32f*)pDst, dstStep, size);
#endif
                default:            return ippStsNumChannelsErr;
                }
#endif
            default: return ippStsDataTypeErr;
            }
        }
        else
        {
            switch(dataType)
            {
#if IW_ENABLE_DATA_TYPE_8U
            case ipp8u:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC_8u_C1RSfs((Ipp8u*)pSrc, srcStep, ownCast_64f8u(*pValue), (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC_8u_C3RSfs((Ipp8u*)pSrc, srcStep, ownCastArray_64f8u(pValue, value, 3), (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC_8u_C4RSfs((Ipp8u*)pSrc, srcStep, ownCastArray_64f8u(pValue, value, 4), (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
                case owniC4M1110:   return ippiMulC_8u_AC4RSfs((Ipp8u*)pSrc, srcStep, ownCastArray_64f8u(pValue, value, 4), (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_16U
            case ipp16u:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC_16u_C1RSfs((Ipp16u*)pSrc, srcStep, ownCast_64f16u(*pValue), (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC_16u_C3RSfs((Ipp16u*)pSrc, srcStep, ownCastArray_64f16u(pValue, value, 3), (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC_16u_C4RSfs((Ipp16u*)pSrc, srcStep, ownCastArray_64f16u(pValue, value, 4), (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
                case owniC4M1110:   return ippiMulC_16u_AC4RSfs((Ipp16u*)pSrc, srcStep, ownCastArray_64f16u(pValue, value, 4), (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_16S
            case ipp16s:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC_16s_C1RSfs((Ipp16s*)pSrc, srcStep, ownCast_64f16s(*pValue), (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC_16s_C3RSfs((Ipp16s*)pSrc, srcStep, ownCastArray_64f16s(pValue, value, 3), (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC_16s_C4RSfs((Ipp16s*)pSrc, srcStep, ownCastArray_64f16s(pValue, value, 4), (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
                case owniC4M1110:   return ippiMulC_16s_AC4RSfs((Ipp16s*)pSrc, srcStep, ownCastArray_64f16s(pValue, value, 4), (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_32F
            case ipp32f:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC_32f_C1R((Ipp32f*)pSrc, srcStep, ownCast_64f32f(*pValue), (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC_32f_C3R((Ipp32f*)pSrc, srcStep, ownCastArray_64f32f(pValue, value, 3), (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC_32f_C4R((Ipp32f*)pSrc, srcStep, ownCastArray_64f32f(pValue, value, 4), (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_AC4
                case owniC4M1110:   return ippiMulC_32f_AC4R((Ipp32f*)pSrc, srcStep, ownCastArray_64f32f(pValue, value, 4), (Ipp32f*)pDst, dstStep, size);
#endif
                default: return ippStsNumChannelsErr;
                }
#endif
            default: return ippStsDataTypeErr;
            }
        }
    }
    else
    {
        if(pSrc == pDst)
        {
#if IPP_VERSION_COMPLEX >= 20170002
            switch(dataType)
            {
#if IW_ENABLE_DATA_TYPE_8U
            case ipp8u:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC64f_8u_C1IR((Ipp8u*)pDst, dstStep, pValue, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC64f_8u_C3IR((Ipp8u*)pDst, dstStep, pValue, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC64f_8u_C4IR((Ipp8u*)pDst, dstStep, pValue, size, algoMode, round);
#endif
                case owniC4M1110:   return ippStsNotSupportedModeErr;
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_16U
            case ipp16u:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC64f_16u_C1IR((Ipp16u*)pDst, dstStep, pValue, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC64f_16u_C3IR((Ipp16u*)pDst, dstStep, pValue, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC64f_16u_C4IR((Ipp16u*)pDst, dstStep, pValue, size, algoMode, round);
#endif
                case owniC4M1110:   return ippStsNotSupportedModeErr;
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_16S
            case ipp16s:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC64f_16s_C1IR((Ipp16s*)pDst, dstStep, pValue, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC64f_16s_C3IR((Ipp16s*)pDst, dstStep, pValue, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC64f_16s_C4IR((Ipp16s*)pDst, dstStep, pValue, size, algoMode, round);
#endif
                case owniC4M1110:   return ippStsNotSupportedModeErr;
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_32F
            case ipp32f:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC64f_32f_C1IR((Ipp32f*)pDst, dstStep, pValue, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC64f_32f_C3IR((Ipp32f*)pDst, dstStep, pValue, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC64f_32f_C4IR((Ipp32f*)pDst, dstStep, pValue, size, algoMode, round);
#endif
                case owniC4M1110:   return ippStsNotSupportedModeErr;
                default:            return ippStsNumChannelsErr;
                }
#endif
            default: return ippStsDataTypeErr;
            }
#else
            return ippStsInplaceModeNotSupportedErr;
#endif
        }
        else
        {
            switch(dataType)
            {
#if IW_ENABLE_DATA_TYPE_8U
            case ipp8u:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC64f_8u_C1R((Ipp8u*)pSrc, srcStep, pValue, (Ipp8u*)pDst, dstStep, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC64f_8u_C3R((Ipp8u*)pSrc, srcStep, pValue, (Ipp8u*)pDst, dstStep, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC64f_8u_C4R((Ipp8u*)pSrc, srcStep, pValue, (Ipp8u*)pDst, dstStep, size, algoMode, round);
#endif
                case owniC4M1110:   return ippStsNotSupportedModeErr;
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_16U
            case ipp16u:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC64f_16u_C1R((Ipp16u*)pSrc, srcStep, pValue, (Ipp16u*)pDst, dstStep, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC64f_16u_C3R((Ipp16u*)pSrc, srcStep, pValue, (Ipp16u*)pDst, dstStep, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC64f_16u_C4R((Ipp16u*)pSrc, srcStep, pValue, (Ipp16u*)pDst, dstStep, size, algoMode, round);
#endif
                case owniC4M1110:   return ippStsNotSupportedModeErr;
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_16S
            case ipp16s:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC64f_16s_C1R((Ipp16s*)pSrc, srcStep, pValue, (Ipp16s*)pDst, dstStep, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC64f_16s_C3R((Ipp16s*)pSrc, srcStep, pValue, (Ipp16s*)pDst, dstStep, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC64f_16s_C4R((Ipp16s*)pSrc, srcStep, pValue, (Ipp16s*)pDst, dstStep, size, algoMode, round);
#endif
                case owniC4M1110:   return ippStsNotSupportedModeErr;
                default:            return ippStsNumChannelsErr;
                }
#endif
#if IW_ENABLE_DATA_TYPE_32F
            case ipp32f:
                switch(chCode)
                {
#if IW_ENABLE_CHANNELS_C1
                case owniC1:        return ippiMulC64f_32f_C1R((Ipp32f*)pSrc, srcStep, pValue, (Ipp32f*)pDst, dstStep, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C3
                case owniC3:        return ippiMulC64f_32f_C3R((Ipp32f*)pSrc, srcStep, pValue, (Ipp32f*)pDst, dstStep, size, algoMode, round);
#endif
#if IW_ENABLE_CHANNELS_C4
                case owniC4:        return ippiMulC64f_32f_C4R((Ipp32f*)pSrc, srcStep, pValue, (Ipp32f*)pDst, dstStep, size, algoMode, round);
#endif
                case owniC4M1110:   return ippStsNotSupportedModeErr;
                default:            return ippStsNumChannelsErr;
                }
#endif
            default: return ippStsDataTypeErr;
            }
        }
    }
}
