/*******************************************************************************
* 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) llwiMul(const void *pSrc1, IwSize src1Step, const void *pSrc2, IwSize src2Step, void *pDst, IwSize dstStep,
                              IwiSize size, IppDataType dataType, int channels, int scaleFactor, IwiChDescriptor chDesc);

/**/////////////////////////////////////////////////////////////////////////////
//                   iwiMul
///////////////////////////////////////////////////////////////////////////// */
IW_DECL(IppStatus) iwiMul(const IwiImage *pSrc1Image, const IwiImage *pSrc2Image, IwiImage *pDstImage, const IwiMulParams *pAuxParams, const IwiTile *pTile)
{
    IppStatus    status;
    IwiMulParams auxParams;

    status = owniCheckImageRead(pSrc1Image);
    if(status)
        return status;
    status = owniCheckImageRead(pSrc2Image);
    if(status)
        return status;
    status = owniCheckImageWrite(pDstImage);
    if(status)
        return status;

    if(pSrc1Image->m_ptrConst == pDstImage->m_ptrConst)
        return ippStsBadArgErr;

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

    if(pSrc1Image->m_dataType == ipp32f)
    {
        if(pSrc1Image->m_channels != pDstImage->m_channels ||
            pSrc2Image->m_typeSize != pDstImage->m_typeSize ||
            pSrc2Image->m_channels != pDstImage->m_channels)
            return ippStsBadArgErr;
    }
    else
    {
        if(pSrc1Image->m_typeSize != pDstImage->m_typeSize ||
            pSrc1Image->m_channels != pDstImage->m_channels ||
            pSrc2Image->m_typeSize != pDstImage->m_typeSize ||
            pSrc2Image->m_channels != pDstImage->m_channels)
            return ippStsBadArgErr;
    }

    {
        const void *pSrc1 = pSrc1Image->m_ptrConst;
        const void *pSrc2 = pSrc2Image->m_ptrConst;
        void       *pDst  = pDstImage->m_ptr;
        IwiSize     size  = owniGetMinSize(&pSrc1Image->m_size, &pDstImage->m_size);
                    size  = owniGetMinSize(&pSrc2Image->m_size, &size);

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

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

                pSrc1 = iwiImage_GetPtrConst(pSrc1Image, dstRoi.y, dstRoi.x, 0);
                pSrc2 = iwiImage_GetPtrConst(pSrc2Image, 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);

                pSrc1 = iwiImage_GetPtrConst(pSrc1Image, srcLim.y, srcLim.x, 0);
                pSrc2 = iwiImage_GetPtrConst(pSrc2Image, srcLim.y, srcLim.x, 0);
                pDst  = iwiImage_GetPtr(pDstImage, dstLim.y, dstLim.x, 0);

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

        if(pSrc1Image->m_dataType == ipp32f && pSrc2Image->m_dataType != ipp32f)
        {
            IwiImage  src1Roi;
            IwiImage  src2Roi;
            IwiImage  dstRoi;
            IwiImage  src2Scaled;
            IwiImage  dstScaled;
            IwiScaleParams scaleParams;
            iwiScale_SetDefaultParams(&scaleParams);
            scaleParams.algoMode = auxParams.algoMode;

            if(auxParams.chDesc != iwiChDesc_None)
                return ippStsNotSupportedModeErr;

            status = iwiImage_InitExternalConst(&src1Roi, size, pSrc1Image->m_dataType, pSrc1Image->m_channels, NULL, pSrc1, pSrc1Image->m_step);
            if(status < 0)
                return status;
            status = iwiImage_InitExternalConst(&src2Roi, size, pSrc2Image->m_dataType, pSrc2Image->m_channels, NULL, pSrc2, pSrc2Image->m_step);
            if(status < 0)
                return status;
            status = iwiImage_InitExternal(&dstRoi, size, pDstImage->m_dataType, pDstImage->m_channels, NULL, pDst, pDstImage->m_step);
            if(status < 0)
                return status;

            iwiImage_Init(&src2Scaled);
            iwiImage_Init(&dstScaled);

            // Rescale wrapper
            for(;;)
            {
                status = iwiImage_Alloc(&src2Scaled, src2Roi.m_size, pSrc1Image->m_dataType, src2Roi.m_channels, NULL);
                if(status < 0)
                    break;
                if(pSrc2 == pDst)
                {
                    status = iwiImage_InitExternal(&dstScaled, src2Scaled.m_size, src2Scaled.m_dataType, src2Scaled.m_channels, &src2Scaled.m_inMemSize, src2Scaled.m_pBuffer, src2Scaled.m_step);
                    if(status < 0)
                        break;
                }
                else
                {
                    status = iwiImage_Alloc(&dstScaled, dstRoi.m_size, pSrc1Image->m_dataType, dstRoi.m_channels, NULL);
                    if(status < 0)
                        break;
                }

                status = iwiScale(&src2Roi, &src2Scaled, 1, 0, &scaleParams, NULL);
                if(status < 0)
                    break;

                status = iwiMul(&src1Roi, &src2Scaled, &dstScaled, &auxParams, NULL);
                if(status < 0)
                    break;

                status = iwiScale(&dstScaled, &dstRoi, 1, 0, &scaleParams, NULL);
                if(status < 0)
                    break;

                break;
            }

            iwiImage_Release(&src2Scaled);
            iwiImage_Release(&dstScaled);

            return status;
        }

        return llwiMul(pSrc1, pSrc1Image->m_step, pSrc2, pSrc2Image->m_step, pDst, pDstImage->m_step, size, pSrc1Image->m_dataType, pSrc1Image->m_channels, auxParams.scaleFactor, auxParams.chDesc);
    }
}


/**/////////////////////////////////////////////////////////////////////////////
//                   Low-Level Wrappers
///////////////////////////////////////////////////////////////////////////// */
IW_DECL(IppStatus) llwiMul(const void *pSrc1, IwSize src1Step, const void *pSrc2, IwSize src2Step, void *pDst, IwSize dstStep,
                              IwiSize size, IppDataType dataType, int channels, int scaleFactor, IwiChDescriptor chDesc)
{
    OwniChCodes chCode = owniChDescriptorToCode(chDesc, channels, channels);

    if(pSrc2 == pDst)
    {
        switch(dataType)
        {
#if IW_ENABLE_DATA_TYPE_8U
        case ipp8u:
            switch(chCode)
            {
#if IW_ENABLE_CHANNELS_C1
            case owniC1:        return ippiMul_8u_C1IRSfs_L((const Ipp8u*)pSrc1, src1Step, (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
            case owniC3:        return ippiMul_8u_C3IRSfs_L((const Ipp8u*)pSrc1, src1Step, (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
            case owniC4:        return ippiMul_8u_C4IRSfs_L((const Ipp8u*)pSrc1, src1Step, (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
            case owniC4M1110:   return ippiMul_8u_AC4IRSfs_L((const Ipp8u*)pSrc1, src1Step, (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 ippiMul_16u_C1IRSfs_L((const Ipp16u*)pSrc1, src1Step, (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
            case owniC3:        return ippiMul_16u_C3IRSfs_L((const Ipp16u*)pSrc1, src1Step, (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
            case owniC4:        return ippiMul_16u_C4IRSfs_L((const Ipp16u*)pSrc1, src1Step, (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
            case owniC4M1110:   return ippiMul_16u_AC4IRSfs_L((const Ipp16u*)pSrc1, src1Step, (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 ippiMul_16s_C1IRSfs_L((const Ipp16s*)pSrc1, src1Step, (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
            case owniC3:        return ippiMul_16s_C3IRSfs_L((const Ipp16s*)pSrc1, src1Step, (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
            case owniC4:        return ippiMul_16s_C4IRSfs_L((const Ipp16s*)pSrc1, src1Step, (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
            case owniC4M1110:   return ippiMul_16s_AC4IRSfs_L((const Ipp16s*)pSrc1, src1Step, (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 ippiMul_32f_C1IR_L((const Ipp32f*)pSrc1, src1Step, (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_C3
            case owniC3:        return ippiMul_32f_C3IR_L((const Ipp32f*)pSrc1, src1Step, (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_C4
            case owniC4:        return ippiMul_32f_C4IR_L((const Ipp32f*)pSrc1, src1Step, (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_AC4
            case owniC4M1110:   return ippiMul_32f_AC4IR_L((const Ipp32f*)pSrc1, src1Step, (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 ippiMul_8u_C1RSfs_L((const Ipp8u*)pSrc1, src1Step, (const Ipp8u*)pSrc2, src2Step, (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
            case owniC3:        return ippiMul_8u_C3RSfs_L((const Ipp8u*)pSrc1, src1Step, (const Ipp8u*)pSrc2, src2Step, (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
            case owniC4:        return ippiMul_8u_C4RSfs_L((const Ipp8u*)pSrc1, src1Step, (const Ipp8u*)pSrc2, src2Step, (Ipp8u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
            case owniC4M1110:   return ippiMul_8u_AC4RSfs_L((const Ipp8u*)pSrc1, src1Step, (const Ipp8u*)pSrc2, src2Step, (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 ippiMul_16u_C1RSfs_L((const Ipp16u*)pSrc1, src1Step, (const Ipp16u*)pSrc2, src2Step, (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
            case owniC3:        return ippiMul_16u_C3RSfs_L((const Ipp16u*)pSrc1, src1Step, (const Ipp16u*)pSrc2, src2Step, (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
            case owniC4:        return ippiMul_16u_C4RSfs_L((const Ipp16u*)pSrc1, src1Step, (const Ipp16u*)pSrc2, src2Step, (Ipp16u*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
            case owniC4M1110:   return ippiMul_16u_AC4RSfs_L((const Ipp16u*)pSrc1, src1Step, (const Ipp16u*)pSrc2, src2Step, (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 ippiMul_16s_C1RSfs_L((const Ipp16s*)pSrc1, src1Step,(const Ipp16s*) pSrc2, src2Step, (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C3
            case owniC3:        return ippiMul_16s_C3RSfs_L((const Ipp16s*)pSrc1, src1Step, (const Ipp16s*)pSrc2, src2Step, (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_C4
            case owniC4:        return ippiMul_16s_C4RSfs_L((const Ipp16s*)pSrc1, src1Step, (const Ipp16s*)pSrc2, src2Step, (Ipp16s*)pDst, dstStep, size, scaleFactor);
#endif
#if IW_ENABLE_CHANNELS_AC4
            case owniC4M1110:   return ippiMul_16s_AC4RSfs_L((const Ipp16s*)pSrc1, src1Step, (const Ipp16s*)pSrc2, src2Step, (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 ippiMul_32f_C1R_L((const Ipp32f*)pSrc1, src1Step, (const Ipp32f*)pSrc2, src2Step, (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_C3
            case owniC3:        return ippiMul_32f_C3R_L((const Ipp32f*)pSrc1, src1Step, (const Ipp32f*)pSrc2, src2Step, (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_C4
            case owniC4:        return ippiMul_32f_C4R_L((const Ipp32f*)pSrc1, src1Step, (const Ipp32f*)pSrc2, src2Step, (Ipp32f*)pDst, dstStep, size);
#endif
#if IW_ENABLE_CHANNELS_AC4
            case owniC4M1110:   return ippiMul_32f_AC4R_L((const Ipp32f*)pSrc1, src1Step, (const Ipp32f*)pSrc2, src2Step, (Ipp32f*)pDst, dstStep, size);
#endif
            default:            return ippStsNumChannelsErr;
            }
#endif
        default: return ippStsDataTypeErr;
        }
    }
}
