/**
* @file CachedExample.h
* @brief data caching of several feature images and many more
* @author Erik Rodner
* @date 04/21/2008

*/
#ifndef CACHEDEXAMPLEINCLUDE
#define CACHEDEXAMPLEINCLUDE

#include <assert.h>
#include <map>

#include "core/vector/SparseVectorT.h"
#include "core/image/MultiChannelImageT.h"
#include "core/basics/FileMgt.h"

namespace OBJREC
{

/** data caching of several feature images and many more,
    can be used in conjunction with Example
    to share image data between different sliding windows within
    an image
    @see Example */
class CachedExample
{

  protected:
    /** resize image to this fixed width */
    int newWidth;
    /** resize image to this fixed height */
    int newHeight;

    /** original image width */
    int oxsize;
    /** original image height */
    int oysize;

    /** filename of image */
    std::string imgfn;

    /** array of double images */
    NICE::MultiChannelImageT<double> *dchannels;

    /** array of integer images */
    NICE::MultiChannelImageT<int> *ichannels;

    /** array of histogram images */
    NICE::MultiChannelImageT<long> *lchannels;

    /** maps for temporary files */
    std::map<int, std::string> dtemps;
    std::map<int, std::string> itemps;
    std::map<int, std::string> ltemps;

    /** read standard image data from file */
    void readImageData ();

    /** read rgb image data from file */
    void readImageDataRGB ();

    /** calc grayvalue integral image */
    void calcIntegralImage ();

    /** array of histogram images */
    NICE::SparseVector **svmap;

    /** sizes of histogram images */
    int *svmap_xsize;
    int *svmap_ysize;

    bool hasColorInformation;

  public:

    /** whether one can obtain color information */
    bool colorInformationAvailable() const;

    enum
    {
      L_INTEGRALIMAGE = 0,
      L_NUMCHANNELS
    };

    /** integer channel types */
    enum
    {
      I_GRAYVALUES = 0,
      I_COLOR,
      I_EDGES,
      I_NUMCHANNELS
    };

    /** double value channels */
    enum
    {
      D_EOH = 0,
      D_INTEGRALPRIOR,
      D_INTEGRALEOH,
      D_INTEGRALCOLOR,
      D_NUMCHANNELS
    };


    /** sparse histogram channel types */
    enum
    {
      SVTEXTON = 0,
      SVNUMCHANNELS
    };

    /** default init method */
    void init ();

    /** simple constructor
        @param imgfn image filename
        @param newWidth resize raw image to this width
        @param newHeight resize raw image to this height
    */
    CachedExample ( const std::string & imgfn, int newWidth = -1,
                    int newHeight = -1 );

    /** constructor (disabled buffering)
        @param img gray-value image
    */
    CachedExample ( const NICE::Image & img );

    /** constructor (disabled buffering)
        @param img rgb image
        @param disableGrayConversion whether to provide gray values or not
    */
    CachedExample ( const NICE::ColorImage & img, bool disableGrayConversion = false );

    /** simple destructor */
    virtual ~CachedExample();

    /**
     * get the NICE::Image Filename
     * @return NICE::Image Filename
     */
    inline std::string getFilename();


    /** get double image channel
        @param channel channel type (choose from enum type)
        @param[out] xsize width of image
        @param[out] ysize height of image
        @return buffer to image data
    */
    inline NICE::MultiChannelImageT<double> & getDChannel ( int channel );

    /** get integer image channel
        @param channel channel type (choose from enum type)
        @param[out] xsize width of image
        @param[out] ysize height of image
        @return buffer to image data
    */
    inline NICE::MultiChannelImageT<int> & getIChannel ( int channel );

    /** get long image channel
        @param channel channel type (choose from enum type)
        @param[out] xsize width of image
        @param[out] ysize height of image
        @return buffer to image data
    */
    inline NICE::MultiChannelImageT<long> & getLChannel ( int channel );

    /** get histogram image
        @param svchannel channel type (choose from histogram channel enum)
        @param[out] xsize width of raw image
        @param[out] ysize height of raw image
        @param[out] tm_xsize width of histogram channel buffer
        @param[out] tm_ysize height of histogram channel buffer
        @remark buffer will be not copied !!
        @return pointer to histogram channel buffer
    */
    NICE::SparseVector *getSVMap ( int svchannel, int & xsize, int & ysize, int & tm_xsize, int & tm_ysize ) const;

    /** assign histogram channel buffer and compute integral image
        @param svchannel
        @param _map pointer to histogram channel buffer
        @param xsize_s width of histogram channel buffer
        @param ysize_s height of histogram channel buffer
        @remark buffer will be not copied !!
    */
    void buildIntegralSV ( int svchannel, NICE::SparseVector *_map, int xsize_s, int ysize_s );

    /** assign histogram channel buffer
    @param svchannel
    @param _map pointer to histogram channel buffer
    @param xsize_s width of histogram channel buffer
    @param ysize_s height of histogram channel buffer
    @remark buffer will be not copied !!
    */
    void setSVMap ( int svchannel, NICE::SparseVector *_map, int xsize_s, int ysize_s );

    /** get image sizes */
    void getImageSize ( int & xsize, int & ysize ) const
    {
      xsize = oxsize;
      ysize = oysize;
    };

    /** drop precached data:
     (1) this is only possible if an image filename is given
     (2) only data channels are deleted that can be reproduced by CachedExample itself
    */
    void dropPreCached();

    template<class ImgPixelValue>
    void dropImages ( NICE::MultiChannelImageT<ImgPixelValue> *images,
                      std::map<int, std::string> & temps,
                      int numImages );
};


/********************** INLINE FUNCTIONS *****************************/
inline NICE::MultiChannelImageT<double> & CachedExample::getDChannel ( int channel )
{
  assert ( ( channel >= 0 ) && ( channel < D_NUMCHANNELS ) );

  if ( dchannels[channel].channels() == 0 )
  {
    std::map<int, std::string>::const_iterator j = dtemps.find ( channel );
    if ( j == dtemps.end() )
    {
      //fprintf (stderr, "NICE::MultiChannelImageT: unable to recover data channel %s (double %d)!\n",
      // imgfn.c_str(), channel);
    }
    else
    {
      //fprintf (stderr, "NICE::MultiChannelImageT: restoring data from %s ", j->second.c_str() );
      dchannels[channel].restore ( j->second );
      //fprintf (stderr, "(%d x %d)\n", dchannels[channel].xsize, dchannels[channel].ysize );
    }
  }

  return dchannels[channel];
}

inline std::string CachedExample::getFilename()
{
  return imgfn;
}

inline NICE::MultiChannelImageT<int> & CachedExample::getIChannel ( int channel )
{
  assert ( ( channel >= 0 ) && ( channel < I_NUMCHANNELS ) );

  if ( ( ichannels[channel].channels() == 0 ) )
  {
    if ( ( imgfn != "" ) && ( channel == I_GRAYVALUES ) )
    {
      readImageData();
    }
    else if ( ( imgfn != "" ) && ( channel == I_COLOR ) )
    {
      readImageDataRGB();
      assert ( hasColorInformation );
    }
    else
    {
      std::map<int, std::string>::const_iterator j = itemps.find ( channel );
      if ( j == itemps.end() )
      {
        //fprintf (stderr, "NICE::MultiChannelImageT: unable to recover data channel (int %d)!\n", channel);
        //exit(-1);
      }
      else
      {
        //fprintf (stderr, "NICE::MultiChannelImageT: restoring data from %s\n", j->second.c_str() );
        ichannels[channel].restore ( j->second );
      }
    }
  }

  return ichannels[channel];
}

inline NICE::MultiChannelImageT<long> & CachedExample::getLChannel ( int channel )
{
  assert ( ( channel >= 0 ) && ( channel < L_NUMCHANNELS ) );

  if ( lchannels[channel].channels() == 0 )
  {
    std::map<int, std::string>::const_iterator j = ltemps.find ( channel );
    if ( j == ltemps.end() )
    {

      if ( channel == L_INTEGRALIMAGE )
      {
        calcIntegralImage();
      }
      else
      {
        //fprintf (stderr, "NICE::MultiChannelImageT: unable to recover data channel (long %d)!\n", channel);
        //exit(-1);
      }
    }
    else
    {
      //fprintf (stderr, "NICE::MultiChannelImageT: restoring data from %s\n", j->second.c_str() );
      lchannels[channel].restore ( j->second );
    }
  }

  return lchannels[channel];
}

template<class ImgPixelValue>
void CachedExample::dropImages ( NICE::MultiChannelImageT<ImgPixelValue> *images, std::map<int, std::string> & temps, int numImages )
{
  for ( int i = 0 ; i < numImages; i++ )
  {
    std::map<int, std::string>::iterator j = temps.find ( i );
    if ( j == temps.end() )
    {
      std::string tempfilename = NICE::FileMgt::createTempFile ( "tmp/cachedexample_%s" );
      //fprintf (stderr, "CachedExample: dumping channel %d/%d to %s (%d x %d)\n", i, numImages, tempfilename.c_str(),
      // images[i].xsize, images[i].ysize );
      images[i].store ( tempfilename );
      temps[i] = tempfilename;
    }
    images[i].freeData();
  }

}



} // namespace

#endif
