/*******************************************************************************
* Copyright 2015-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 "ippcore_tl.h"
#include "owndefs_tl.h"

#if defined( USE_TBB )
#include "tbb/parallel_for.h"
#include "tbb/task_arena.h"
#include "tbb/blocked_range.h"

static int threads_of_ipp_tl = -1;

int IPP_CDECL ownGetNumThreads_LT(void)
{
    if (threads_of_ipp_tl < 1)
    {
#if TBB_INTERFACE_VERSION >= 9100
        threads_of_ipp_tl = tbb::this_task_arena::max_concurrency();
#else
        threads_of_ipp_tl =      tbb::task_arena::max_concurrency();
#endif
    }
    return threads_of_ipp_tl;
}

int IPP_CDECL ownGetThreadIdx_LT(void)
{
#if TBB_INTERFACE_VERSION >= 9100
    return tbb::this_task_arena::current_thread_index();
#else
    return      tbb::task_arena::current_thread_index();
#endif
}

IPPFUN(IppStatus, ippGetThreadingType_LT, (IppThreadingType *thrType))
{
    if (thrType == 0) return ippStsNullPtrErr;
    *thrType = TBB;
    return ippStsNoErr;
}

IPPFUN(IppStatus, ippGetThreadingType_T, (IppThreadingType *thrType))
{
    return ippGetThreadingType_LT (thrType);
}

IPPFUN(IppStatus, ippGetNumThreads_LT, (int* pNumThr))
{
    if (pNumThr == 0) return ippStsNullPtrErr;
    *pNumThr = ownGetNumThreads_LT();
    return ippStsNoErr;
}

IPPFUN(IppStatus, ippGetNumThreads_T, (int* pNumThr))
{
    return ippGetNumThreads_LT(pNumThr);
}

IPPFUN(IppStatus, ippGetThreadIdx_LT, (int* pThrIdx))
{
    if (pThrIdx == 0) return ippStsNullPtrErr;
    *pThrIdx = ownGetThreadIdx_LT();
    return ippStsNoErr;
}

IPPFUN(IppStatus, ippGetThreadIdx_T, (int* pThrIdx))
{
    return ippGetThreadIdx_LT(pThrIdx);
}

class applyFunc_l {
    functype_l my_func;
    void * my_arg;
    IppStatus & my_status;
public:
    void operator()( const tbb::blocked_range<IppSizeL>& r ) const {
        functype_l func = my_func;
        void * arg = my_arg;
        IppStatus & status = my_status;
        for( IppSizeL index = r.begin(); index != r.end(); ++index ) {
            IppStatus threadStatus = func(index, arg);
            if (threadStatus != ippStsNoErr) status = threadStatus;
        }
    }
    applyFunc_l( IppStatus & status, void * arg, functype_l func ) :
        my_func(func),
        my_arg(arg),
        my_status(status)
    {}
};

IPPFUN(IppStatus, ippParallelFor_LT, (IppSizeL numTiles, void* arg, functype_l func))
{
    if (arg == 0) return ippStsNullPtrErr;
    if (func == 0) return ippStsNullPtrErr;

    IppStatus status = ippStsNoErr;

    tbb::parallel_for(tbb::blocked_range<IppSizeL>(0, numTiles), applyFunc_l (status, arg, func));
    return status;
}

class applyFunc {
    functype my_func;
    void * my_arg;
    IppStatus & my_status;
public:
    void operator()( const tbb::blocked_range<int>& r ) const {
        functype func = my_func;
        void * arg = my_arg;
        IppStatus & status = my_status;
        for( int index = r.begin(); index != r.end(); ++index ) {
            IppStatus threadStatus = func(index, arg);
            if (threadStatus != ippStsNoErr) status = threadStatus;
        }
    }
    applyFunc( IppStatus & status, void * arg, functype func ) :
        my_func(func),
        my_arg(arg),
        my_status(status)
    {}
};

IPPFUN(IppStatus, ippParallelFor_T, (int numTiles, void* arg, functype func))
{
    if (arg == 0) return ippStsNullPtrErr;
    if (func == 0) return ippStsNullPtrErr;

    IppStatus status = ippStsNoErr;

    tbb::parallel_for(tbb::blocked_range<int>(0, numTiles), applyFunc (status, arg, func));
    return status;
}

#endif
