#ifndef IMAGE_OP_H
#define IMAGE_OP_H

#include "image.h"
#include "multi_array_op.h"

/** @addtogroup arrayOp
    @{ */
/** @page imageOperators Operators for mimas::image.
    Element-wise operations for 2-dimensional images are implemented using the
    @ref arrayOperators "operators for boost::multiarray".

    Functionality, which is specifically related to two-dimensional images,
    is implemented for \c mimas::image.

    Algorithms, which can also be applied to n-dimensional arrays, are
    implemented for \c boost::multi_array. Corresponding wrappers for
    forwarding the calls are implemented for \c mimas::image.

    @author Jan Wedekind <jan@wedesoft.de> 
    @date Mon Jun 13 17:19:18 UTC 2005
    @todo Add documentation of all available image-operators. */

namespace mimas {

///
template<
  typename T, class F
>
image_ref< T > &image_apply( image_ref< T > &_img, F f )
{
  if ( _img.initialised() ) {
    boost::multi_array_ref< T, 2 > data( _img.rawData(),
                                         boost::extents[ _img.getHeight() ]
                                                       [ _img.getWidth() ] );
    multi_apply( data, f );
  };
  return _img;
}

///
template<
  typename T1, typename T2, class F,
  typename T2Ptr
>
image_ref< T1 > &image_apply( image_ref< T1 > &a,
                              const const_image_ref< T2, T2Ptr > &b, F f )
{
  if ( a.initialised() && b.initialised() ) {
    boost::multi_array_ref< T1, 2 >
      da( a.rawData(), boost::extents[ a.getHeight() ][ a.getWidth() ] );
    boost::const_multi_array_ref< T2, 2 >
      db( b.rawData(), boost::extents[ b.getHeight() ][ b.getWidth() ] );
    multi_apply( da, db, f );
  };
  return a;
}

///
template<
  typename T1, typename T2, typename T3, class F,
  typename T2Ptr, typename T3Ptr
>
image_ref< T1 > &image_apply( image_ref< T1 > &a,
                              const const_image_ref< T2, T2Ptr > &b,
                              const const_image_ref< T3, T3Ptr > &c, F f )
{
  if ( a.initialised() && b.initialised() && c.initialised() ) {
    boost::multi_array_ref< T1, 2 >
      da( a.rawData(), boost::extents[ a.getHeight() ][ a.getWidth() ] );
    boost::const_multi_array_ref< T2, 2 >
      db( b.rawData(), boost::extents[ b.getHeight() ][ b.getWidth() ] );
    boost::const_multi_array_ref< T3, 2 >
      dc( c.rawData(), boost::extents[ c.getHeight() ][ c.getWidth() ] );
    multi_apply( da, db, dc, f );
  };
  return a;
}

template<
  typename T1, typename T2, class F,
  typename T2Ptr
>
image< T1 > image_func( const const_image_ref< T2, T2Ptr > &a, F f )
{
  image< T1 > retVal; retVal.init( a.getWidth(), a.getHeight() );
  image_apply( retVal, a, _multi_help1< T1, T2, F >( f ) );
  return retVal;
}

template<
  typename T1, typename T2, typename T3, class F,
  typename T2Ptr, typename T3Ptr
>
  image< T1 > image_func( const const_image_ref< T2, T2Ptr > &a,
                          const const_image_ref< T3, T3Ptr > &b,
                          F f )
{
  image< T1 > retVal; retVal.init( a.getWidth(), a.getHeight() );
  image_apply( retVal, a, b, _multi_help2< T1, T2, T3, F >( f ) );
  return retVal;
}

};
///@}

#define __MIMASINTERNALIMAGEFUNC operator*=
#define __MIMASEXTERNALIMAGEFUNC operator*
#define __MIMASFUNCTIONOBJECT std::multiplies
#include "image_op_help.h"

#define __MIMASINTERNALIMAGEFUNC operator/=
#define __MIMASEXTERNALIMAGEFUNC operator/
#define __MIMASFUNCTIONOBJECT std::divides
#include "image_op_help.h"

#define __MIMASINTERNALIMAGEFUNC operator+=
#define __MIMASEXTERNALIMAGEFUNC operator+
#define __MIMASFUNCTIONOBJECT std::plus
#include "image_op_help.h"

#define __MIMASINTERNALIMAGEFUNC operator-=
#define __MIMASEXTERNALIMAGEFUNC operator-
#define __MIMASFUNCTIONOBJECT std::minus
#include "image_op_help.h"

#define __MIMASEXTERNALIMAGEFUNC absolute
#define __MIMASINTERNALIMAGEFUNC absoluteIt
#define __MIMASFUNCTIONOBJECT _abs
#include "image_op_help2.h"

#define __MIMASEXTERNALIMAGEFUNC conj
#define __MIMASINTERNALIMAGEFUNC conjIt
#define __MIMASFUNCTIONOBJECT _conj
#include "image_op_help2.h"

#define __MIMASEXTERNALIMAGEFUNC sqr
#define __MIMASINTERNALIMAGEFUNC sqrIt
#define __MIMASFUNCTIONOBJECT _sqr
#include "image_op_help2.h"

#define __MIMASEXTERNALIMAGEFUNC logarithm
#define __MIMASINTERNALIMAGEFUNC logarithmIt
#define __MIMASFUNCTIONOBJECT _log
#include "image_op_help2.h"

#define __MIMASEXTERNALIMAGEFUNC squareRoot
#define __MIMASINTERNALIMAGEFUNC squareRootIt
#define __MIMASFUNCTIONOBJECT _sqrt
#include "image_op_help2.h"

#define __MIMASEXTERNALIMAGEFUNC sumSquares
#define __MIMASFUNCTIONOBJECT _sumsquares
#include "image_op_help3.h"

#define __MIMASEXTERNALIMAGEFUNC orientation
#define __MIMASFUNCTIONOBJECT _orientation
#include "image_op_help3.h"

namespace mimas {

/** @addtogroup arrayOp
    @{ */
///
template <
  typename T1, typename T2, typename T2Ptr
>
image< T1 > norm( const const_image_ref< T2, T2Ptr > &a )
{
  return image_func< T1 >( a, _norm< T1, T2 >() );
}

///
template <
  typename T1, typename T2
>
image< T1 > arg( const image< T2 > &a )
{
  return image_func< T1 >( a, _arg< T1, T2 >() );
}

///
template <
  typename T
>
image< int > fastSqr( const image< T > &a )
{
  return image_func< int >( a, _fastsqr< T >() );
};

}

#endif