/**
* @file CachedExample.cpp
* @brief data caching
* @author Erik Rodner, Sven Sickert
* @date 04/21/2008 (modified 03/18/2016)

*/
#include <iostream>

#include "CachedExample.h"
#include "vislearning/baselib/Conversions.h"
#include "vislearning/baselib/ProgressBar.h"

#include "vislearning/image/GenericImageTools.h"
#include "vislearning/baselib/Preprocess.h"
#include "vislearning/baselib/ICETools.h"

using namespace OBJREC;

void CachedExample::init ()
{
    dchannels = new NICE::MultiChannelImageT<double> [D_NUMCHANNELS];
    ichannels = new NICE::MultiChannelImageT<int> [I_NUMCHANNELS];
    lchannels = new NICE::MultiChannelImageT<long> [L_NUMCHANNELS];

    dchannels3 = new NICE::MultiChannelImage3DT<double> [D_NUMCHANNELS];
    ichannels3 = new NICE::MultiChannelImage3DT<int> [I_NUMCHANNELS];
    lchannels3 = new NICE::MultiChannelImage3DT<long> [L_NUMCHANNELS];

    svmap = new NICE::SparseVector *[SVNUMCHANNELS];
    svmap_xsize = new int [SVNUMCHANNELS];
    svmap_ysize = new int [SVNUMCHANNELS];
    for ( uint k = 0 ; k < SVNUMCHANNELS ; k++ )
    {
        svmap[k] = NULL;
        svmap_xsize[k] = 0;
        svmap_ysize[k] = 0;
    }
}

CachedExample::CachedExample ( const std::string & _imgfn,
                               int _newWidth,
                               int _newHeight )
{
    imgfn.push_back(_imgfn);
    newWidth = _newWidth;
    newHeight = _newHeight;
    newDepth = 1;
    Preprocess::getImageSize ( _imgfn, oxsize, oysize );
    ozsize = 1;
    init();
    hasColorInformation = true;
}

CachedExample::CachedExample (
        const std::vector<std::string> & _imgfn,
        int _newWidth,
        int _newHeight,
        int _newDepth )
{
    imgfn = _imgfn;
    newWidth = _newWidth;
    newHeight = _newHeight;
    newDepth = _newDepth;
    Preprocess::getImageSize ( _imgfn[0], oxsize, oysize );
    ozsize = _imgfn.size();
    init();
    hasColorInformation = true;
}

CachedExample::CachedExample ( const NICE::Image & _img )
{
    imgfn.push_back("");
    newWidth = -1;
    newHeight = -1;
    newDepth = -1;
    init();

    oxsize = _img.width();
    oysize = _img.height();
    ozsize = 1;

    ichannels[I_GRAYVALUES].freeData();
    ichannels[I_GRAYVALUES].addChannel(_img);
    hasColorInformation = false;
}

CachedExample::CachedExample (
        const NICE::MultiChannelImageT<int> & _img )
{
    newWidth = -1;
    newHeight = -1;
    newDepth = -1;
    init();

    oxsize = _img.width();
    oysize = _img.height();
    ozsize = _img.channels();

    for ( unsigned int i = 0; i < ozsize; i++ )
        imgfn.push_back("");

    ichannels3[I_GRAYVALUES].freeData();
    ichannels3[I_GRAYVALUES].addChannel(_img);
    hasColorInformation = false;
}

CachedExample::CachedExample ( const NICE::ColorImage & img, bool disableGrayConversion )
{
    imgfn.push_back("");
    oxsize = img.width();
    oysize = img.height();
    ozsize = 1;
    newWidth = -1;
    newHeight = -1;
    newDepth = -1;
    init();

    if ( ! disableGrayConversion )
    {
        // refactor-nice.pl: check this substitution
        // old: Image imggray;
        NICE::Image imggray;
        ICETools::calcGrayImage ( img, imggray );

        ichannels[I_GRAYVALUES].freeData();
        ichannels[I_GRAYVALUES].addChannel(imggray);
    }

    ichannels[I_COLOR].reInit ( oxsize, oysize, 3);

    for ( int y = 0 ; y < oysize ; y++ )
        for ( int x = 0 ; x < oxsize ; x++ )
        {
            ichannels[I_COLOR](x,y,0) = img.getPixel ( x, y, 0 );
            ichannels[I_COLOR](x,y,1) = img.getPixel ( x, y, 1 );
            ichannels[I_COLOR](x,y,2) = img.getPixel ( x, y, 2 );
        }

    hasColorInformation = true;
}

CachedExample::CachedExample (
        const NICE::MultiChannelImage3DT<int> & img,
        bool disableGrayConversion )
{
    oxsize = img.width();
    oysize = img.height();
    ozsize = img.depth();
    newWidth = -1;
    newHeight = -1;
    newDepth = -1;
    for ( unsigned int i = 0; i < ozsize; i++ )
        imgfn.push_back("");

    init();

    int ochannels = img.channels();

    if ( ! disableGrayConversion && (ochannels > 2) )
    {
        ichannels3[I_GRAYVALUES].freeData();
        ichannels3[I_GRAYVALUES].addChannel(1);
        for ( int z = 0; z < ozsize; z++ )
        {
            NICE::Image imggray;
            ICETools::calcGrayImage ( img.getColor(z), imggray );

            for ( int y = 0; y < oysize; y++ )
                for ( int x = 0; x < oxsize; x++ )
                    ichannels3[I_GRAYVALUES](x,y,z,0) = imggray.getPixel(x,y);
        }
    }

    ichannels3[I_COLOR].addChannel ( img );

    hasColorInformation = true;
}

CachedExample::~CachedExample()
{
    delete [] dchannels;
    delete [] ichannels;
    delete [] lchannels;

    delete [] dchannels3;
    delete [] ichannels3;
    delete [] lchannels3;

    for ( uint k = 0 ; k < SVNUMCHANNELS ; k++ )
        if ( svmap[k] != NULL )
            delete [] ( svmap[k] );

    delete [] svmap;
    delete [] svmap_xsize;
    delete [] svmap_ysize;

    // remove all temporary files
    for ( std::map<int, std::string>::const_iterator j = dtemps.begin();
          j != dtemps.end();
          j++ )
    {
        //fprintf (stderr, "CachedExample: removing temp file %s\n", j->second.c_str() );
        NICE::FileMgt::deleteTempFile ( j->second );
    }

    for ( std::map<int, std::string>::const_iterator j = itemps.begin();
          j != itemps.end();
          j++ )
    {
        //fprintf (stderr, "CachedExample: removing temp file %s\n", j->second.c_str() );
        NICE::FileMgt::deleteTempFile ( j->second );
    }

    for ( std::map<int, std::string>::const_iterator j = ltemps.begin();
          j != ltemps.end();
          j++ )
    {
        //fprintf (stderr, "CachedExample: removing temp file %s\n", j->second.c_str() );
        NICE::FileMgt::deleteTempFile ( j->second );
    }

}

void CachedExample::readImageData ()
{
    ozsize = imgfn.size();

    if ( ozsize == 0 || imgfn[0] == "" )
        return;

    for ( int z = 0; z < ozsize; z++ )
    {
        NICE::Image orig = Preprocess::ReadImgAdv ( imgfn[z] );
        NICE::Image imggray;

        if ( newWidth > 0 )
            Conversions::resizeImage ( orig, imggray, newWidth, newHeight );
        else
            imggray = orig;

        if ( z == 0 )
        {
            oxsize = imggray.width();
            oysize = imggray.height();

            if ( ozsize > 1 )
                ichannels3[I_GRAYVALUES].reInit( oxsize, oysize, ozsize, 1);
            else
            {
                ichannels[I_GRAYVALUES].freeData();
                ichannels[I_GRAYVALUES].addChannel(imggray);
            }
        }

        if ( ozsize > 1 )
            for ( int y = 0; y < oysize; y++ )
                for ( int x = 0; x < oxsize; x++ )
                    ichannels3[I_GRAYVALUES](x,y,z,0) = imggray.getPixel(x,y);
    }
}

void CachedExample::readImageDataRGB ()
{
    ozsize = imgfn.size();

    if ( ozsize == 0 || imgfn[0] == "" )
        return;

    for ( int z = 0; z < ozsize; z++ )
    {
        NICE::ColorImage img;
        try {
            img = Preprocess::ReadImgAdvRGB ( imgfn[z] );
        } catch ( NICE::ImageException & ) {
            fprintf ( stderr, "error reading rgb image %s\n", imgfn[z].c_str() );
            hasColorInformation = false;
            return;
        }

        if ( z == 0 )
        {
            oxsize = img.width();
            oysize = img.height();

            hasColorInformation = true;

            if ( ozsize > 1 )
                ichannels3[I_COLOR].reInit ( oxsize, oysize, ozsize, 3);
            else
                ichannels[I_COLOR].reInit ( oxsize, oysize, 3);
        }

        for ( int y = 0; y < oysize; y++ )
            for ( int x = 0; x < oxsize; x++ )
            {
                if ( ozsize > 1 )
                {
                    ichannels3[I_COLOR](x,y,z,0) = img.getPixel(x,y,0);
                    ichannels3[I_COLOR](x,y,z,1) = img.getPixel(x,y,1);
                    ichannels3[I_COLOR](x,y,z,2) = img.getPixel(x,y,2);
                }
                else
                {
                    ichannels[I_COLOR](x,y,0) = img.getPixel ( x, y, 0 );
                    ichannels[I_COLOR](x,y,1) = img.getPixel ( x, y, 1 );
                    ichannels[I_COLOR](x,y,2) = img.getPixel ( x, y, 2 );
                }
            }
    }
}

void CachedExample::calcIntegralImage ()
{
    // in case of standard 2D images
    if ( ozsize == 1 )
    {
        if ( ichannels[I_GRAYVALUES].width() == 0 )
        {
            readImageData ();
            if ( ichannels[I_GRAYVALUES].width() == 0 )
            {
                fprintf ( stderr, "CachedExample::getChannel: unable to recover data channel\n" );
                exit ( -1 );
            }
        }

        lchannels[L_INTEGRALIMAGE].reInit (
                    ichannels[I_GRAYVALUES].width(),
                    ichannels[I_GRAYVALUES].height(),
                    1 );

        NICE::ImageT<long int> tmp = lchannels[L_INTEGRALIMAGE][0];

        GenericImageTools::calcIntegralImage (
                    tmp,
                    ichannels[I_GRAYVALUES][0],
                    ichannels[I_GRAYVALUES].width(),
                    ichannels[I_GRAYVALUES].height() );
    }
    // in case of a 3D images
    else
    {
        if ( ichannels[I_GRAYVALUES].width() == 0 )
        {
            readImageData ();
            if ( ichannels[I_GRAYVALUES].width() == 0 )
            {
                fprintf ( stderr, "CachedExample::getChannel: unable to recover data channel\n" );
                exit ( -1 );
            }
        }

        int nwidth = ichannels3[I_GRAYVALUES].width();
        int nheight = ichannels3[I_GRAYVALUES].height();
        int ndepth = ichannels3[I_GRAYVALUES].depth();
        int nchannels = ichannels3[I_GRAYVALUES].channels();
        lchannels3[L_INTEGRALIMAGE].reInit( nwidth, nheight, ndepth, nchannels);
        for ( int c = 0; c < nchannels; c++ )
        {
            for ( int z = 0; z < ndepth; z++ )
                for ( int y = 0; y < nheight; y++ )
                    for ( int x = 0; x < nwidth; x++ )
                    {
                        lchannels3[L_INTEGRALIMAGE](x,y,z,c) =
                                (long)ichannels3[L_INTEGRALIMAGE].get(x,y,z,c);
                    }

            lchannels3[L_INTEGRALIMAGE].calcIntegral(c);
        }
    }
}

void CachedExample::buildIntegralSV ( int svchannel,
                                      NICE::SparseVector *_map,
                                      int xsize_s, int ysize_s )
{
    NICE::SparseVector *map = _map;
    svmap[svchannel] = _map;
    svmap_xsize[svchannel] = xsize_s;
    svmap_ysize[svchannel] = ysize_s;

    int k = xsize_s;
    for ( int y = 1 ; y < ysize_s; y++, k += xsize_s )
        map[k].add ( ( map[k-xsize_s] ) );

    k = 1;
    for ( int x = 1 ; x < xsize_s; x++, k++ )
        map[k].add ( ( map[k-1] ) );

    k = xsize_s + 1;

    for ( int y = 1 ; y < ysize_s ; y++, k++ )
    {
        for ( int x = 1 ; x < xsize_s ; x++, k++ )
        {
            map[k].add ( ( map[k-1] ) );
            map[k].add ( ( map[k-xsize_s] ) );
            map[k].add ( ( map[k-xsize_s-1] ), -1.0 );
        }
    }
}

void CachedExample::setSVMap ( int svchannel,
                               NICE::SparseVector *_map,
                               int xsize_s, int ysize_s )
{
    svmap[svchannel] = _map;
    svmap_xsize[svchannel] = xsize_s;
    svmap_ysize[svchannel] = ysize_s;
}

NICE::SparseVector *CachedExample::getSVMap ( int svchannel,
                                        int & _xsize, int & _ysize,
                                        int & _tm_xsize, int & _tm_ysize ) const
{
    _xsize = oxsize;
    _ysize = oysize;
    _tm_xsize = svmap_xsize[svchannel];
    _tm_ysize = svmap_ysize[svchannel];
    assert ( svmap[svchannel] != NULL );
    return svmap[svchannel];
}

bool CachedExample::colorInformationAvailable() const
{
    if ( hasColorInformation ) return true;
    else {
        if ( imgfn.size() == 0 ) return false;

        //     int tmp_xsize, tmp_ysize, tmp_maxval, tmp_nr;
        // refactor: InfImgFile ( imgfn, tmp_xsize, tmp_ysize, tmp_maxval, tmp_nr );
        NICE::ImageFile imgf ( imgfn[0] );
        const NICE::ImageFile::Header & imgfheader = imgf.getHeader();
        //     tmp_xsize = imgfheader.width;
        //     tmp_ysize = imgfheader.height;
        //     tmp_maxval = 255;
        int tmp_nr = imgfheader.channel;

        if ( tmp_nr > 1 ) return true;
        else return false;
    }
}

void CachedExample::dropPreCached()
{
    dropImages<double> ( dchannels, dtemps, D_NUMCHANNELS );
    dropImages<int> ( ichannels, itemps, I_NUMCHANNELS );
    dropImages<long> ( lchannels, ltemps, L_NUMCHANNELS );
}
