/*@@@**************************************************************************
* \file  image
* \author Hernan Badino
* \date  Tue Apr 21 20:46:39 GMT 2009
* \notes 
*******************************************************************************
******************************************************************************/

/* INCLUDES */
#include "image.h"
#include <cstring>

#include <stdlib.h>
//#include <cstring>

using namespace VIC;

#ifdef CVIMAGE_MEMORY_CHECK
std::map<unsigned char *, unsigned int> VIC::CImageBase::m_memoryMap_s;
#endif

/// Default constructor.
CImageBase::CImageBase ( )
        : m_data_p (                     NULL ),
          m_width_ui (                      0 ),
          m_height_ui (                     0 ),
          m_allocated_b (               false ),
          m_comment_str (                  "" ),
          m_bytesPerPixel_ui8 (             1 ),
          m_format_e (           IF_LUMINANCE ),
          m_dataType_e (            IDT_UBYTE )
{
}

/// Constructor with size.
CImageBase::CImageBase ( unsigned int f_width_ui,
                         unsigned int f_height_ui )
        : m_data_p (                     NULL ),
          m_width_ui (             f_width_ui ),
          m_height_ui (           f_height_ui ),
          m_allocated_b (               false ),
          m_comment_str (                  "" ),
          m_bytesPerPixel_ui8 (             1 ),
          m_format_e (           IF_LUMINANCE ),
          m_dataType_e (            IDT_UBYTE )
{
    ensureAllocation ();
}

/// Constructor with size.
CImageBase::CImageBase ( S2D<unsigned int> size )
        : m_data_p (                     NULL ),
          m_width_ui (             size.width ),
          m_height_ui (           size.height ),
          m_allocated_b (               false ),
          m_comment_str (                  "" ),
          m_bytesPerPixel_ui8 (             1 ),
          m_format_e (           IF_LUMINANCE ),
          m_dataType_e (            IDT_UBYTE )
{
    ensureAllocation ();
}

/// Copy constructor.
CImageBase::CImageBase ( const CImageBase & f_other )
        : m_data_p (                     NULL ),
          m_allocated_b (               false )
{
    copyFrom ( f_other, false );
}

/// Copy constructor.
CImageBase::CImageBase ( const CImageBase & f_other, 
                 bool           f_allocate_b )
        : m_data_p (                     NULL ),
          m_allocated_b (               false )
{
    copyFrom ( f_other, f_allocate_b );
}


/// Destructor.
CImageBase::~CImageBase ( )
{
    freeMemory();
}

/// Copy from other image.
bool 
CImageBase::copyImageContentFrom ( const CImageBase & f_other )
{
    /// Check data 
    if ( m_data_p == NULL ||
         f_other.m_data_p == NULL  )
    {
        //printf("data is null\n");
        return false;
    }
    
    /// Check if same data.
    if ( m_data_p == f_other.m_data_p )
    {
        //printf("data is equal\n");
        return true;
    }

    /// Check size of image.
    if ( m_width_ui != f_other.m_width_ui ||
         m_height_ui < f_other.m_height_ui ||
         m_bytesPerPixel_ui8  != f_other.m_bytesPerPixel_ui8 )
    {
        //printf("data is incompatible\n");
        return false;
    }
    
    size_t bufferSize_ui = m_width_ui * f_other.m_height_ui * m_bytesPerPixel_ui8;
    
    memcpy ( m_data_p, 
             f_other.m_data_p, 
             bufferSize_ui );

    return true;
}

/// Copy from other image.
bool 
CImageBase::copyImageContentTo ( CImageBase & fr_other ) const
{
    /// Check data 
    if ( m_data_p == NULL ||
         fr_other.m_data_p == NULL  )
    {
        //printf("data is null\n");
        return false;
    }
    
    /// Check if same data.
    if ( m_data_p == fr_other.m_data_p )
    {
        //printf("data is equal\n");
        return true;
    }

    /// Check size of image.
    if ( m_width_ui != fr_other.m_width_ui ||
         m_height_ui < fr_other.m_height_ui ||
         m_bytesPerPixel_ui8  != fr_other.m_bytesPerPixel_ui8 )
    {
        //printf("data is incompatible\n");
        return false;
    }
    
    size_t bufferSize_ui = m_width_ui * fr_other.m_height_ui * m_bytesPerPixel_ui8;
    
    memcpy ( fr_other.m_data_p, 
             m_data_p, 
             bufferSize_ui );

    return true;
}

/// Copy from other image.
bool 
CImageBase::copyFrom ( const CImageBase & f_other, const bool f_allocate_b )
{
    m_width_ui           = f_other.m_width_ui;
    m_height_ui          = f_other.m_height_ui;
    m_comment_str        = f_other.m_comment_str;
    m_bytesPerPixel_ui8  = f_other.m_bytesPerPixel_ui8;
    m_format_e           = f_other.m_format_e;
    m_dataType_e         = f_other.m_dataType_e;

    freeMemory();

    if ( not f_allocate_b )
    {
        m_allocated_b = false;
        m_data_p      = f_other.m_data_p;
    }
    else
    {
        size_t bufferSize_ui = m_width_ui * m_height_ui * m_bytesPerPixel_ui8;

        m_data_p = new uint8_t [bufferSize_ui];

        m_allocated_b = true;

        if ( m_data_p == NULL )
            return false;

        memcpy ( m_data_p, 
                 f_other.m_data_p, 
                 bufferSize_ui );
    }

#ifdef CVIMAGE_MEMORY_CHECK
    ++m_memoryMap_s[m_data_p];

    printf("   IMAGE POINTER = %p has %i copies (in copyFrom)\n",
           m_data_p, m_memoryMap_s[m_data_p] );
#endif

    return true;
}

/// Operator =.
CImageBase & 
CImageBase::operator = ( const CImageBase & f_other )
{
    /// Copy without allocating.
    copyFrom ( f_other, false );
    return *this;
}

/// Free memory.
void
CImageBase::freeMemory()
{
#ifdef CVIMAGE_MEMORY_CHECK
    unsigned int copies_ui = 1;
    MemoryMap_t::iterator it = m_memoryMap_s.find ( m_data_p );

    if (it != m_memoryMap_s.end())
        copies_ui = (--it->second);
#endif

    if ( m_data_p ) //&& copies_ui == 0 )
    {
#ifdef CVIMAGE_MEMORY_CHECK
        if ( copies_ui == 0 )
            m_memoryMap_s.erase(it);
#endif

        if ( m_allocated_b )
        {
            delete [] m_data_p;
        }        
    }

#ifdef CVIMAGE_MEMORY_CHECK
    printf("Total elements in memory map is %u\n", m_memoryMap_s.size() );
#endif

    m_allocated_b = false;
    m_data_p      = NULL;
}

/// Clear image data.
void
CImageBase::clear()
{    
    if ( m_data_p == NULL )
        return;
    
    size_t bufferSize_ui = m_width_ui * m_height_ui * m_bytesPerPixel_ui8;
    
    memset ( m_data_p, 
             0, 
             bufferSize_ui );
}


/// Ensure the self-allocation of memory.
bool
CImageBase::ensureAllocation ( bool f_copyData_b /* = false */ )
{
    // If not allocated but we are the only copy of the image then
    // we own the allocation.
    //if ( !m_allocated_b && m_memoryMap_s[m_data_p] == 1 )
    //    m_allocated_b = true;

    if ( !m_allocated_b || !m_data_p )
    {
        uint8_t *  externData_p = m_data_p;

        freeMemory();

        size_t bufferSize_ui = m_width_ui * m_height_ui * m_bytesPerPixel_ui8;        

        m_data_p = new uint8_t [bufferSize_ui];

#ifdef CVIMAGE_MEMORY_CHECK
        ++m_memoryMap_s[m_data_p];

        printf("   IMAGE POINTER = %p has %i copies (in copyFrom)\n",
             m_data_p, m_memoryMap_s[m_data_p] );
#endif

        if ( m_data_p == NULL )
            return false;

        m_allocated_b = true;

        if ( externData_p && f_copyData_b )
            memcpy ( m_data_p, 
                     externData_p, 
                     bufferSize_ui );
    }
    
    return true;
}

/// Check if other image has same format.
bool CImageBase::hasSameFormat ( const CImageBase & f_other ) const
{
    return ( m_width_ui           == f_other.m_width_ui && 
             m_height_ui          == f_other.m_height_ui &&
             m_bytesPerPixel_ui8  == f_other.m_bytesPerPixel_ui8 &&
             m_format_e           == f_other.m_format_e &&
             m_dataType_e         == f_other.m_dataType_e );
}


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