/*@@@**************************************************************************
** \file  gaussianPyramid_inline
* \date   Mon Sep 28 15:59:11 EDT 2009
* \author Hernan Badino
* \notes  
*******************************************************************************
*****          (C) COPYRIGHT Hernan Badino - All Rights Reserved          *****
******************************************************************************/

#if defined ( _OPENMP )
#include <omp.h>
#endif

#include <math.h>
//#include "logger.h"

template <class ImgDType_>
inline
CGaussianPyramid<ImgDType_>::CGaussianPyramid ( )
        : CImagePyramid<ImgDType_> ( )
{
    CImagePyramid<ImgDType_>::setAllocated0Level(false);
}

template <class ImgDType_>
inline
CGaussianPyramid<ImgDType_>::CGaussianPyramid (  unsigned int f_width_ui,
                                           unsigned int f_height_ui,
                                           unsigned int f_levels_ui )
        : CImagePyramid<ImgDType_> ( f_width_ui, f_height_ui, f_levels_ui, false )
{
}

template <class ImgDType_>
inline
CGaussianPyramid<ImgDType_>::~CGaussianPyramid ( )
{
}

template <class ImgDType_>
inline bool 
CGaussianPyramid<ImgDType_>::compute ( const CTypedImage<ImgDType_> &f_img )
{
    if(  f_img.getWidth()  > CImagePyramid<ImgDType_>::getWidth() ||
         f_img.getHeight() > CImagePyramid<ImgDType_>::getHeight() )
    {
        printf("The image is larger than the allocated size of the "
               "pyramids\n");
        
        return false;
    }

    /// Set first the first level with the original image.
    CImagePyramid<ImgDType_>::m_images_p[0] = (CTypedImage<ImgDType_> *) &f_img;
    
    if ( CImagePyramid<ImgDType_>::m_levels_ui <= 1) return true;
    
    for (unsigned int i = 1 ; i < CImagePyramid<ImgDType_>::m_levels_ui; ++i)
    {
        bool success_b = computeNextLevel ( CImagePyramid<ImgDType_>::m_images_p[i-1],
                                            CImagePyramid<ImgDType_>::m_images_p[i  ] );
        if (!success_b)
            return false;
    }    

    return false;    
}

template <>
inline bool 
CGaussianPyramid<float>::computeNextLevel ( const CTypedImage<float> * const f_src_p,
                                            CTypedImage<float> * const       f_dst_p )
{                                               
    IppiSize roiSize;
    
    roiSize.width  = f_src_p->getWidth() - 4;
    roiSize.height = f_src_p->getHeight() - 4;
    
    IppStatus status_i = 
        ippiPyrDown_Gauss5x5_32f_C1R ( f_src_p->getScanline(0)+0,
                                       f_src_p->getWidth() * sizeof(float),
                                       f_dst_p->getScanline(0)+0,
                                       f_dst_p->getWidth() * sizeof(float),
                                       roiSize,
                                       m_buffer_p );
    if ( status_i != ippStsNoErr)
    {
        printf("IPP failed:\n");
        printIPPError( status_i );
        return false;
    }

    return true;
}

template <>
inline bool 
CGaussianPyramid<unsigned char>::computeNextLevel ( const CTypedImage<unsigned char> * const f_src_p,
                                                    CTypedImage<unsigned char> * const       f_dst_p )
{                                               
    IppiSize roiSize;
    
    roiSize.width  = f_src_p->getWidth();
    roiSize.height = f_src_p->getHeight();
    
    IppStatus status_i = 
        ippiPyrDown_Gauss5x5_8u_C1R ( f_src_p->getData(),
                                      f_src_p->getWidth() * sizeof(unsigned char),
                                      f_dst_p->getData(), 
                                      f_dst_p->getWidth() * sizeof(unsigned char),
                                      roiSize,
                                      m_buffer_p );
    if ( status_i != ippStsNoErr)
    {
        printf("IPP failed:\n");
        printIPPError( status_i );
        return false;
    }

    return true;
}

template <>
inline bool 
CGaussianPyramid<signed char>::computeNextLevel ( const CTypedImage<signed char> * const f_src_p,
                                                  CTypedImage<signed char> * const       f_dst_p )
{                                               
    IppiSize roiSize;
    
    roiSize.width  = f_src_p->getWidth();
    roiSize.height = f_src_p->getHeight();
    
    IppStatus status_i = 
        ippiPyrDown_Gauss5x5_8s_C1R ( f_src_p->getData(),
                                      f_src_p->getWidth() * sizeof(signed char),
                                      f_dst_p->getData(), 
                                      f_dst_p->getWidth() * sizeof(signed char),
                                      roiSize,
                                      m_buffer_p );
    if ( status_i != ippStsNoErr)
    {
        printf("IPP failed:\n");
        printIPPError( status_i );
        return false;
    }

    return true;
}

/// There is no implementation of PyrDown in current Ipp version 5.2
/// for short int. So apply the two steps: gaussian computation and downsampling.
/// NOTE!!!!! NOTE !!!! NOTE!!!! Ipp only provides gauss filtering with signed
/// short int. If you are using the whole 16 bits in a unsigned short int, 
/// the result might be wrong!
template <>
inline bool 
CGaussianPyramid<unsigned short int>::computeNextLevel ( const CTypedImage<unsigned short int> * const f_src_p,
                                                         CTypedImage<unsigned short int> * const       f_dst_p )
{                                               
    IppiSize srcSize;
    IppiRect srcRoi;
    IppiSize dstSize;
     
    srcSize.width  = f_src_p->getWidth();
    srcSize.height = f_src_p->getHeight();

    srcRoi.x = 1;
    srcRoi.y = 1;
    srcRoi.width  = srcSize.width - 2;
    srcRoi.height = srcSize.height - 2;
    
    dstSize.width  = f_dst_p -> getWidth();
    dstSize.height = f_dst_p -> getHeight();
    
    printf ("Src = %p, dst = %p, width = %i height = %i\n",
            f_src_p->getScanline(1)+1,
            m_buffer_p, srcRoi.width, srcRoi.height );

    IppStatus status_i = 
        ippiFilterGauss_16s_C1R ( (signed short int *)f_src_p->getScanline(1)+1,
                                  f_src_p->getWidth() * sizeof(signed short int),
                                  (signed short int *) m_buffer_p,
                                  f_src_p->getWidth() * sizeof(signed short int),
                                  dstSize,
                                  ippMskSize3x3 );

    if ( status_i != ippStsNoErr)
    {
        printf("IPP failed:\n");
        printIPPError( status_i );
        return false;
    }

    status_i = 
        ippiResize_16u_C1R ( ( unsigned short int *) m_buffer_p,
                             srcSize,
                             f_src_p->getWidth() * sizeof(unsigned short int),
                             srcRoi,
                             f_dst_p->getData(), 
                             f_dst_p->getWidth() * sizeof(unsigned short int),
                             dstSize, 
                             .5, .5, 
                             IPPI_INTER_NN );
    
    if ( status_i != ippStsNoErr)
    {
        printf("IPP failed:\n");
        printIPPError( status_i );
        return false;
    }

    return true;
}

template <class ImgDType_>
inline void
CGaussianPyramid<ImgDType_>::setAllocated0Level ( bool /*f_allocate_b*/ )
{
}


#define CGAUSSPYR_DEF_FUNC_GETDATATYPE(suffix_) \
    template <>                                 \
    inline IppDataType                          \
    CGaussianPyramid<I##suffix_>::getDataType() \
    {                                           \
        return (IppDataType) i##suffix_;        \
    }

CGAUSSPYR_DEF_FUNC_GETDATATYPE(pp8u)
CGAUSSPYR_DEF_FUNC_GETDATATYPE(pp16u)
CGAUSSPYR_DEF_FUNC_GETDATATYPE(pp32u)
CGAUSSPYR_DEF_FUNC_GETDATATYPE(pp8s)
CGAUSSPYR_DEF_FUNC_GETDATATYPE(pp16s)
CGAUSSPYR_DEF_FUNC_GETDATATYPE(pp32s)
CGAUSSPYR_DEF_FUNC_GETDATATYPE(pp32f)
CGAUSSPYR_DEF_FUNC_GETDATATYPE(pp64s)
CGAUSSPYR_DEF_FUNC_GETDATATYPE(pp64u)
CGAUSSPYR_DEF_FUNC_GETDATATYPE(pp64f)


/* ////////////  Version History ///////////////
 *  $Log: gaussianPyramid_inline.h,v $
 *  Revision 1.3  2010/01/18 17:12:29  badino
 *  General updates and added new classes.
 *
 *  Revision 1.2  2009/11/18 15:51:01  badino
 *  badino: documentation added. Some other global changes.
 *
 *//////////////////////////////////////////////
