/*@@@**************************************************************************
* \file  seqDevHDImg
* \author Hernan Badino
* \date  Wed Apr 22 15:44:40 GMT 2009
* \notes 
*******************************************************************************
******************************************************************************/

/* INCLUDES */
#include <QDir>
#include <QTimer>

#include "seqDevHDImg.h"
#include "paramIOFile.h"
#include "logger.h"

using namespace VIC;


CSeqDevHDImg::CSeqDevHDImg()
        : m_dialog_p (                          NULL ),
          m_qtPlay_p (                          NULL ),
          m_currentFrame_i (                      -1 ),
          m_framesCount_i (                        0 ),
          m_backward_b (                       false ),
          m_imagesPerFrame_uc (                    0 ),
          m_seqName_str (                         "" )
{
    for (int i = 0 ; i < m_maxImgsPerFrame_uc; ++i )
    {
        m_output_p[i] = 0;
    }
    
    m_qtPlay_p = new QTimer ( this );
    connect(m_qtPlay_p, SIGNAL(timeout()), this, SLOT(timeOut()));

    //connect(m_dialog_p, SIGNAL(reset()), this, SLOT(stopAndEmitReset()));

    m_currentState_e = S_PAUSED;
}

/// Destructor
CSeqDevHDImg::~CSeqDevHDImg()
{
    freeImages();
}

void
CSeqDevHDImg::timeOut()
{
    if (m_backward_b)
        prevFrame();
    else
        nextFrame();

    emit cycle( );
}

/// Initialize Device.
bool 
CSeqDevHDImg::initialize()
{
    m_currentFrame_i = 0;

    return loadCurrentFrame();
}

/// Load next frame
bool CSeqDevHDImg::nextFrame()
{
    ++m_currentFrame_i;

    // Autoexit
    if (0 && m_currentFrame_i == m_framesCount_i)
        exit(0);

    m_currentFrame_i %= m_framesCount_i;
    return loadCurrentFrame();
}

/// Load previous frame
bool CSeqDevHDImg::loadCurrentFrame()
{
    bool res_b = true;
    
    for (int i = 0 ; i < m_imagesPerFrame_uc; ++i)
    {
        if ( m_fileName_p[i].size() > (unsigned int) m_currentFrame_i )
        {
            std::string fullPathFile_str = m_directoryPath_p[i];
            fullPathFile_str += "/";
            fullPathFile_str += m_fileName_p[i][m_currentFrame_i];

            m_filePaths_p[i] = (std::string) fullPathFile_str;

            if (!loadImageFile ( fullPathFile_str, m_output_p[i] ))
            {
                res_b = false;
                logger::warn("File << file << could not be read");
            }

            m_timeStamps_p[i] = getTimeStampFromFilename(m_fileName_p[i][m_currentFrame_i]);
        }
    }

    return res_b;
}

double CSeqDevHDImg::getTimeStampFromFilename( std::string f_fileName_p )
{
    double tt_d = 0;
    int fieldsRead_i =  sscanf( f_fileName_p.c_str()+13, "%lfus", 
                                &tt_d );
    
    if (fieldsRead_i == 1)
        return tt_d/1000000.0;    

    return -1.0;
}


bool CSeqDevHDImg::loadImageFile( std::string f_filePath_str, CImage * f_image_p )
{
    printf("Loading file %s\n", f_filePath_str.c_str());
    
    typedef enum { Pbm = 1, Pgm, Ppm, PbmRaw, PgmRaw, PpmRaw } PnmFormat_t;
    
    FILE* file_p;
    PnmFormat_t format_e;

    if ( (file_p = fopen(f_filePath_str.c_str(), "rb")) == NULL)
    {
        printf("Can't open image file %s\n", f_filePath_str.c_str());
        return false;       
    }
    
    int temp_i;    
    int argNum_i = fscanf(file_p, "P%i\n", &temp_i );
    format_e = (PnmFormat_t) temp_i;

    if (format_e != PgmRaw || argNum_i != 1)
    {
        printf("By now only raw pgm format can be read.\n");
        fclose (file_p);
        printf("Can't open image file %s\n", f_filePath_str.c_str());
        return false;
    }
 
    char *dataStr_p = (char *)malloc(1024);
    size_t buffSize_i = 0;
    std::string comment_str;
        
    while ( getline ( &dataStr_p, &buffSize_i, file_p ) > 0 )
    {
        if (dataStr_p[0] != '#' && dataStr_p[0] != '\n' )
            break;
        comment_str += dataStr_p+1;
    }
    
    unsigned int width_i, height_i, greyLevels_i;
    
    if ( sscanf(dataStr_p, "%i %i\n", &width_i, &height_i ) != 2 )
    {
        fclose (file_p);
        printf( "Incorrect format of image file %s (width: %i height: %i)\n", 
                f_filePath_str.c_str(),
                width_i,
                height_i );
        return false;
    }

    free(dataStr_p);    

    if (fscanf(file_p, "%i\n", &greyLevels_i ) != 1 )
    {
        fclose (file_p);
        printf( "Incorrect format of image file %s (width: %i height: %i greylevels: %i)\n", 
                f_filePath_str.c_str(),
                width_i,
                height_i,
                greyLevels_i );
        return false;
    }

    int bpp_i = greyLevels_i > 255?2:1;    
    
    printf( "Reading image data content from file %s (width: %i height: %i greylevels: %i)\n", 
            f_filePath_str.c_str(),
            width_i,
            height_i,
            greyLevels_i );

    if ( f_image_p -> getWidth ()        != (unsigned int)width_i ||
         f_image_p -> getHeight ()       != (unsigned int)height_i ||
         f_image_p -> getBytesPerPixel() != (unsigned int)bpp_i )
    {
        f_image_p -> freeMemory();

        f_image_p -> setWidth         ( width_i );
        f_image_p -> setHeight        ( height_i );
        f_image_p -> setBytesPerPixel ( bpp_i );
    }

    f_image_p -> setComment ( comment_str );
    f_image_p -> ensureAllocation();

    f_image_p -> setImageFormat ( CImage::IF_LUMINANCE );
    f_image_p -> setDataType    ( bpp_i == 1?
                                  (CImage::IDT_UBYTE):(CImage::IDT_USHORT) );

    unsigned int size_ui = width_i * height_i * bpp_i;

    if (fread( f_image_p -> getData( ), size_ui, 1, file_p) != 1)
    {
        fclose (file_p);
        printf("Incorrect format of file %s\n", f_filePath_str.c_str());
        return false;
    }

    printf("Comment String is :\n%s\n", comment_str.c_str());
    
    if ( comment_str.find ("bigEndian") != (unsigned int)-1 && 
         bpp_i > 1  )
    {
        printf("Converting big endian to little endian.\n");
        unsigned char *byte_p = f_image_p -> getData( );
        unsigned char *end_p  = f_image_p -> getData( ) + size_ui;
        unsigned char tmp_uc;

        /// This can be optimized with openmp.
        while ( byte_p != end_p )
        {
            tmp_uc = *byte_p;
            *byte_p = *(byte_p+1);
            ++byte_p;
            *byte_p = tmp_uc;
            ++byte_p;
        }
    } 

    fclose(file_p);
    
    return true;
    
}

/// Load previous frame
bool CSeqDevHDImg::prevFrame()
{
    m_currentFrame_i += m_framesCount_i - 1;
    m_currentFrame_i %= m_framesCount_i;
    return loadCurrentFrame();
}

/// Load next frame
bool CSeqDevHDImg::goToFrame( int f_frameNumber_i )
{
    if ( f_frameNumber_i > 0 && f_frameNumber_i <= m_framesCount_i )
    {
        m_currentFrame_i = f_frameNumber_i - 1;
        return loadCurrentFrame();
    }
    
    return false;
}

/// Stop/Stand
bool CSeqDevHDImg::stop()
{
    m_currentFrame_i = 0;
    m_currentState_e = S_PAUSED;
    // Stop timer.
    m_qtPlay_p -> stop();    
    return true;
}

/// Play
bool CSeqDevHDImg::startPlaying()
{
    m_currentState_e = S_PLAYING;
    m_backward_b = false;

    if ( not m_qtPlay_p -> isActive() )
        m_qtPlay_p -> start(1);

    return true;
}

/// Play Backwards
bool CSeqDevHDImg::startPlayingBackward()
{
    m_currentState_e = S_PLAYING_BACKWARD;
    m_backward_b = true;

    if ( not m_qtPlay_p -> isActive() )
        m_qtPlay_p -> start(1);

    return true;
}

/// Pause
bool CSeqDevHDImg::pause()
{
    m_currentState_e = S_PAUSED;
    m_qtPlay_p -> stop();
    return true;
}

/*
void CSeqDevHDImg::reset()
{

}
*/

/// Get number of frames in this sequence.
int CSeqDevHDImg::getNumberOfFrames() const
{
    return m_framesCount_i;
}
 
/// Get current frame in the sequence.
int CSeqDevHDImg::getCurrentFrame() const
{
    return m_currentFrame_i+1;
}

/// Is a forward/backward device?
bool CSeqDevHDImg::isBidirectional() const
{
    return true;
}

/// Get the dialogs of this device.
std::vector<QDialog *> CSeqDevHDImg::getDialogs ( ) const
{
    return std::vector<QDialog *>();
}

bool CSeqDevHDImg::registerOutputs ( 
        std::map< std::string, CIOObj* > &fr_map )
{
    char txt[256];
    
    for ( uint8_t i = 0 ; i < m_imagesPerFrame_uc ; ++i)
    {
        sprintf(txt, "Image %i", i);
        fr_map[txt] = m_output_p[i];

        sprintf(txt, "Image %i Path", i);
        fr_map[ txt ] = m_filePaths_p + i;

        sprintf(txt, "Image %i Timestamp", i);
        fr_map[ txt ] = m_timeStamps_p + i;
    }
    
    m_imgNr = (uint32_t)m_currentFrame_i;
    fr_map[ "Frame Number" ] = &m_imgNr;
    

    return true;
}

bool 
CSeqDevHDImg::loadNewSequence ( const std::string &f_confFilePath_str )
{
    CParamIOFile  paraReader;
    char paramName_str[256];
    bool ok_b;

    ok_b = paraReader.load ( f_confFilePath_str );

    freeImages();
    
    /// Get directory path from full path.
    std::string f_pathToConfFile_str = f_confFilePath_str;

    int pos_i = f_pathToConfFile_str.find_last_of ("/\\");

    printf("loading new sequence %i\n", pos_i);
    
    if ( pos_i != -1 )
        f_pathToConfFile_str.erase(pos_i);
    else
        f_pathToConfFile_str = std::string(".");
    
    printf("f_pathToConfFile_str = %s\n", f_pathToConfFile_str.c_str());

    ok_b = paraReader.setCurrentCategory ( "Image Sequence" );

    if (!ok_b)
    {
        //logger::warn ( "Category \"Sequence\" was not found in %s.", 
        //               f_confFilePath_str );
        printf ( "Category \"Image Sequence\" was not found in %s.\n", 
                 f_confFilePath_str.c_str() );
        m_imagesPerFrame_uc = 0;
    }
    else
    {
        int i;
        
        /// Lets load the image paths.
        for ( i = 0 ; i < m_maxImgsPerFrame_uc ; ++i )
        {
            sprintf ( paramName_str, "Relative Directory %i", i );
            std::string dir_str;
            ok_b = paraReader.get ( paramName_str, dir_str );
            if (not ok_b) break;
            
            sprintf ( paramName_str, "Filter Image %i", i );
            std::string filter_str;
            ok_b = paraReader.get ( paramName_str, filter_str );
            if (not ok_b) break;
            
            m_directoryPath_p[i] = f_pathToConfFile_str;
            m_directoryPath_p[i] += "/";
            m_directoryPath_p[i] += dir_str;
            printf("  directory %s\n", m_directoryPath_p[i].c_str());
        
            findFiles ( m_directoryPath_p[i], filter_str, m_fileName_p[i] );
        }
        
        if (i == 0)
        {
            //logger::warn ( "No entries for image sequences have been found in %s.", 
            //               f_confFilePath_str );
            printf ( "No entries for image sequences have been found in %s.", 
                     f_confFilePath_str.c_str() );
        }
        else
        {
            printf("Sequence read from configuration file:\n");

            /// update the number of frames, give corresponding warnings if number
            /// of images is not equal, etc...
            m_framesCount_i = 0;
            
            for (int j = 0 ; j < i; ++j)
            {
                m_framesCount_i = std::max(m_framesCount_i, 
                                           (int)m_fileName_p[j].size() );

                printf("\tDirectory: %s - number of files %i\n", 
                       m_directoryPath_p[j].c_str(),
                       (int)m_fileName_p[j].size());
            }
        }
        
        m_imagesPerFrame_uc = i;
        allocateImages();

        emit start();
    }
    
    return m_imagesPerFrame_uc != 0;
}

void 
CSeqDevHDImg::findFiles ( std::string    f_fullPath_str, 
                          std::string    filter_str, 
                          StringList_t  &fr_fileNames ) const
{
    QDir        dir (f_fullPath_str.c_str());
    QString     qsFilter (filter_str.c_str());
    QStringList qslFilters;
    QStringList qslImageFiles;

    qslFilters << qsFilter;

    qslImageFiles = dir.entryList(qslFilters, QDir::Files | QDir::Readable);

    /// Transform to standard vector.
    for (int i = 0; i < qslImageFiles.count(); ++i)
    {
        fr_fileNames.push_back ( qslImageFiles[i].toStdString() );
    }    
}

void 
CSeqDevHDImg::freeImages()
{
    for (unsigned int i = 0; i < m_imagesPerFrame_uc; ++i)
    {
        if (m_output_p[i])
            delete m_output_p[i];
        m_output_p[i] = NULL;
    }
    
}

void 
CSeqDevHDImg::allocateImages()
{
    for (unsigned int i = 0; i < m_imagesPerFrame_uc; ++i)
    {
        m_output_p[i] = new CImage;
    }
}



 

/* ////////////  Version History ///////////////
 *  $Log: seqDevHDImg.cpp,v $
 *  Revision 1.2  2009/11/18 15:50:14  badino
 *  badino: global changes.
 *
 *//////////////////////////////////////////////
