#ifndef __CIMAGEBASE_H
#define __CIMAGEBASE_H

/**
 *******************************************************************************
 *
 * @file imagerBase.h
 *
 * \class CImageBase
 * \date  Tue Oct 13, 2009
 * \author Hernan Badino (hernan.badino@gmail.com)
 *
 * \brief Class for handling images.
 *
 * The class implements a basic image having a size, description, 
 * the image data, data type and image format. The data might be own 
 * or externally allocated. Destruction of the buffer is automatically handled 
 * Copy constructors as well as a assignment operator are defined.
 *
 ******************************************************************************/

/* INCLUDES */
#include <vector>
#include <map>
#include <string>
#include <cassert>

#include "ioObj.h"
#include "standardTypes.h"

#include "glheader.h"

/* CONSTANTS */

namespace VIC
{
    /// 
    
    class CImageBase: public CIOObj
    {
    /// Some public enumerators.
    public:
        typedef enum 
        {
            IF_OTHER           = -12345,
            IF_LUMINANCE       = GL_LUMINANCE,
            IF_LUMINANCE_ALPHA = GL_LUMINANCE_ALPHA,
            IF_RGB             = GL_RGB,
            IF_RGBA            = GL_RGBA
        } ImageFormat_t;

        typedef enum
        {
            IDT_OTHER  = -987654321,
            IDT_BYTE   = GL_BYTE,
            IDT_UBYTE  = GL_UNSIGNED_BYTE,
            IDT_SHORT  = GL_SHORT,
            IDT_USHORT = GL_UNSIGNED_SHORT,
            IDT_INT    = GL_INT,
            IDT_UINT   = GL_UNSIGNED_INT,
            IDT_FLOAT  = GL_FLOAT,
            IDT_DOUBLE = GL_DOUBLE
       } DataType_t;
        
    /// Constructors/Destructor
    public:

        /// Default constructor.
        CImageBase ( );

        /// Copy constructor.
        CImageBase ( const CImageBase & f_other );

        /// Constructor with size.
        CImageBase ( unsigned int f_width_ui,
                     unsigned int f_height_ui );

        /// Constructor with size.
        CImageBase ( S2D<unsigned int> size );

        /// Copy constructor.
        CImageBase ( const CImageBase & f_other, 
                     bool               m_allocate_b );

        /// Destructor.
        virtual ~CImageBase ( );

    /// Gets/Sets
    public:
        /// Set Size.
        void          setSize( unsigned int f_width_ui,
                               unsigned int f_height_ui );

         /// Set Size.
        void          setSize( S2D<unsigned int> size );


         /// Get Size.
        S2D<unsigned int> 
                      getSize( ) const;

        /// Get Width.
        unsigned int  getWidth( ) const;

        /// Set Width.
        void          setWidth( unsigned int f_width_ui );

        /// Get Height.
        unsigned int  getHeight( ) const;

        /// Set Height.
        void          setHeight( unsigned int f_height_ui );

        /// Get Data.
        void *        getDataPointer( ) const;

        /// Get Scanline
        void *        getScanlinePointer ( unsigned int i ) const;

        /// Set Data.
        void          setData( void *f_data_p );

        /// Get Comment.
        std::string   getComment( ) const;

        /// Set Comment.
        void          setComment( const std::string &f_comment_str );

        /// Get bytes per pixel.
        uint8_t       getBytesPerPixel( ) const;

        /// Set bytes per pixel.
        void          setBytesPerPixel( uint8_t f_number_ui8 );

        /// Get image format.
        ImageFormat_t getImageFormat( ) const;

        /// Set bytes per pixel.
        void          setImageFormat( ImageFormat_t f_format_e );

        /// Get image format.
        DataType_t    getDataType( ) const;

        /// Set bytes per pixel.
        void          setDataType( DataType_t f_type_e );

        /// Has its own data or was copied from other image.
        bool          hasOwnData ( ) const;
        
    /// Operators
    public:
       /// Operator =.
        CImageBase        &operator = ( const CImageBase & f_other );
        

    /// Help methods.
    public:
        /// Copy from other image.
        bool          copyFrom ( const CImageBase & f_other,
                                 const bool         f_allocate_b = true );

        /// Copy from other image.
        bool          copyImageContentFrom ( const CImageBase & f_other );

        /// Copy from other image.
        bool          copyImageContentTo ( CImageBase & fr_other ) const;

        /// Ensure the self-allocation of memory.
        bool          ensureAllocation (  bool f_copyData_b = false );

        /// Free memory.
        void          freeMemory();

        /// Clear image.
        void          clear();

        /// Check if other image has same format.
        bool          hasSameFormat ( const CImageBase & f_other ) const;


    /// Static methods.
    public:
        float  getValueFromPtr ( unsigned char * f_ptr_p );

    /// Protected methods.
    protected:
        
    /// Protected static.
    protected:
#ifdef CVIMAGE_MEMORY_CHECK
        typedef std::map<unsigned char *, unsigned int> MemoryMap_t;
        static MemoryMap_t m_memoryMap_s;
#endif
    /// Protected members.
    protected:

        /// Image data.
        uint8_t *         m_data_p;

        /// Width of the image
        unsigned int      m_width_ui;

        /// Height of the image
        unsigned int      m_height_ui;

        /// Allocated or extern data.
        bool              m_allocated_b;

        /// Allocated or extern data.
        std::string       m_comment_str;        

        /// Number of bits per pixel.
        uint8_t           m_bytesPerPixel_ui8;

        /// Number of bits per pixel.
        ImageFormat_t     m_format_e;

        /// Number of bits per pixel.
        DataType_t        m_dataType_e;
    };


    /// Set Image size.
    inline
    void CImageBase::setSize( unsigned int f_width_ui,
                              unsigned int f_height_ui )
    {
        m_width_ui  = f_width_ui;
        m_height_ui = f_height_ui;
    }

    /// Set Image size.
    inline
    void  CImageBase::setSize( S2D<unsigned int> size )
    {
        m_width_ui  = size.width;
        m_height_ui = size.height;
    }
    
    /// Get Image size.
    inline
    S2D<unsigned int> CImageBase::getSize( ) const
    {
        return S2D<unsigned int> (m_width_ui, m_height_ui);
    }

    /// Get Width.
    inline
    unsigned int CImageBase::getWidth( ) const
    {
        return m_width_ui;
    }

    /// Set Width.
    inline
    void CImageBase::setWidth( unsigned int f_width_ui )
    {
        m_width_ui = f_width_ui;
    }

    /// Get Height.
    inline
    unsigned int CImageBase::getHeight( ) const
    {
        return m_height_ui;
    }

    /// Set Height.
    inline
    void CImageBase::setHeight( unsigned int f_height_ui )
    {
        m_height_ui = f_height_ui;
    }    

    /// Get Data.
    inline
    void * CImageBase::getDataPointer( ) const
    {
        return (void *) m_data_p;
    }

    /// Get Data.
    inline
    void * CImageBase::getScanlinePointer ( unsigned int i ) const
    {
        return (void *)( m_data_p + 
                         m_bytesPerPixel_ui8 * i * m_width_ui );
    }

    /// Set Data.
    inline
    void CImageBase::setData( void *f_data_p )
    {
        freeMemory();
        m_data_p = (uint8_t *) f_data_p;
        m_allocated_b = false;
        
#ifdef CVIMAGE_MEMORY_CHECK
        ++m_memoryMap_s[m_data_p];
#endif
    }
    
    /// Get Comment.
    inline
    std::string CImageBase::getComment( ) const
    {
        return m_comment_str;
    }

    /// Set Comment.
    inline
    void CImageBase::setComment( const std::string &f_comment_str )
    {
        m_comment_str = f_comment_str;
    }

    /// Get bytes per pixel.
    inline
    uint8_t CImageBase::getBytesPerPixel( ) const
    {
        return m_bytesPerPixel_ui8;
    }

    /// Set bytes per pixel.
    inline
    void CImageBase::setBytesPerPixel( uint8_t f_number_ui8 )
    {
        m_bytesPerPixel_ui8 = f_number_ui8;
     }

    /// Get bytes per pixel.
    inline
    CImageBase::ImageFormat_t CImageBase::getImageFormat( ) const
    {
        return m_format_e;
    }

    /// Set bytes per pixel.
    inline
    void CImageBase::setImageFormat( CImageBase::ImageFormat_t f_format_e )
    {
        m_format_e = f_format_e;
    }

    /// Get bytes per pixel.
    inline
    CImageBase::DataType_t CImageBase::getDataType( ) const
    {
        return m_dataType_e;
    }

    /// Set bytes per pixel.
    inline
    void CImageBase::setDataType( CImageBase::DataType_t f_type_e )
    {
        m_dataType_e = f_type_e;
    }

    inline
    bool CImageBase::hasOwnData ( ) const
    {
        return m_allocated_b;
    }
    


    inline
    float CImageBase::getValueFromPtr ( unsigned char * f_ptr_p )
    {
        if ( m_format_e != GL_LUMINANCE ) return 0.f;
        
        switch( m_dataType_e)
        {
            case IDT_BYTE:
            {
                return (float) *((char*) (f_ptr_p));
            }
            break;
            
            case IDT_UBYTE:
            {
                return (float) *((unsigned char*) (f_ptr_p));
            }
            break;
            
            case IDT_SHORT:
            {
                return (float) *((short int*) (f_ptr_p));
            }
            break;
            
            case IDT_USHORT:
            {
                return (float) *((unsigned short int*) (f_ptr_p));
            }
            break;
            
            case IDT_INT:
            {
                return (float) *((int*) (f_ptr_p));
            }
            break;
            
            case IDT_UINT:
            {
                return (float) *((unsigned int*) (f_ptr_p));
            }
            break;
            
            case IDT_FLOAT:
            {
                return (float) *((float*) (f_ptr_p));
            }
            break;

            default:
                return 0.f;
       }
        
    }
    
}


#endif // __CIMAGEBASE_H

/* ////////////  Version History ///////////////
 *  $Log: imageBase.h,v $
 *  Revision 1.2  2009/11/18 15:51:01  badino
 *  badino: documentation added. Some other global changes.
 *
 *//////////////////////////////////////////////
