/*************************************************************************************************/
/*!
   	@file		imagefileloader_psd.h
	@author 	Fanzo
 	@date 		2008/3/25
*/
/*************************************************************************************************/
#pragma		once

///////////////////////////////////////////////////////////////////////////////////////////////////
//include files
#include	"iFace/iLoaderImagefile.h"

#pragma pack( push , 8 )		//set align

namespace icubic
{

///////////////////////////////////////////////////////////////////////////////////////////////////
// preprocessor deifne

///////////////////////////////////////////////////////////////////////////////////////////////////
// type define

///////////////////////////////////////////////////////////////////////////////////////////////////
// classes define

/**************************************************************************************************
"LoaderImagefile_psd" class 
**************************************************************************************************/
class LoaderImagefile_psd : 
	virtual public object_base , 
	public ILoaderImagefile
{
// query
	query_begin();
	iface_hook( ILoaderImagefile , ILoaderImagefile_IID )
	query_end( object_base );
	
// variable member
private:
	int							m_version;
	int							m_channel;
	DPI							m_dpi;
	isize						m_size;
	int							m_mode;
	int							m_depth;

	Straightdata<Array<uint8>>	m_image;

// private functions
private:
//=================================================================================================
//!	free
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void Free()
{
	m_version	= 0;
	m_channel	= 0;
	m_size		= isize();
	m_mode		= 0;
	m_depth		= 0;
	m_image.Resize( 0 );
}
//=================================================================================================
//!	read FileHeaderSection
//!	@retval			---
//-------------------------------------------------------------------------------------------------
bool FileHeaderSection
		(
		iFileStreamRead&	reader
		)
{
	// Signature
	{
		char	sig[ 4 ];
		if( 4 != reader->Read( sig , sizeof( char ) , 4 , Big_EndianType ) )
			return false;
		if ( sig[ 0 ] != '8' || sig[ 1 ] != 'B' || sig[ 2 ] != 'P' || sig[ 3 ] != 'S' )
			return false;
	}
	// Version
	{
		short	ver;
		if( 1 != reader->Read( &ver , sizeof( ver ) , 1 , Big_EndianType ) )
			return false;
		if ( ver != 1 )
			return false;
		m_version	= ver;
	}
	
	// Resurved
	{
		char	resurved[ 6 ];
		int		i;
		if( 6 != reader->Read( resurved , sizeof( char ) , 6 , Big_EndianType ) )
			return false;
		for ( i = 0 ; i < 6 ; i++ )
		{
			if ( resurved[ i ] != 0 )
				return false;
		}
	}
	// Channel
	{
		short	channel;
		if( 1 != reader->Read( &channel , sizeof( channel ) , 1 , Big_EndianType ) )
			return false;
		if ( channel < 1 || channel > 24 )
			return false;
		m_channel	= channel;
	}
	// Rows
	{
		long	row;
		if( 1 != reader->Read( &row , sizeof( row ) , 1 , Big_EndianType ) )
			return false;
		if( row < 0 || row > 30000 )
			return false;
		m_size.height	= row;
	}
	// Column
	{
		long	column;
		if( 1 != reader->Read( &column , sizeof( column ) , 1 , Big_EndianType ) )
			return false;
		if ( column < 0 || column > 30000 )
			return false;
		m_size.width	= column;
	}
	// Depth
	{
		short	depth;
		if( 1 != reader->Read( &depth , sizeof( depth ) , 1 , Big_EndianType ) )
			return false;
		if ( depth != 1 && depth != 8 && depth != 16 )
			return false;
		m_depth	= depth;
	}
	// Mode
	{
		short	mode;
		if( 1 != reader->Read( &mode , sizeof( mode ) , 1 , Big_EndianType ) )
			return false;
		if ( mode != 0 && mode != 1 && mode != 2 && mode != 3 && mode != 4  && mode != 7  && mode != 8  && mode != 9 )
			return false;
		m_mode	= mode;
	}
	return true;
}
//=================================================================================================
//!	read color mode section.
//!	@retval			---
//-------------------------------------------------------------------------------------------------
bool ColorModeSection
		(
		iFileStreamRead&	reader
		)
{
	// get length of the section.
	unsigned long	len;
	if( 1 != reader->Read( &len , sizeof( len ) , 1 , Big_EndianType ) )
		return false;
	
	// seek read pointer.
	if( false == reader->Seek( reader->GetPosition() + len ) )
		return false;
	return true;
}
//=================================================================================================
//!	image resource section.
//!	@retval			---
//-------------------------------------------------------------------------------------------------
bool ImageResourceSection
		(
		iFileStreamRead&	reader
		)
{
	// get length of the section.
	unsigned long	len;
	if( 1 != reader->Read( &len , sizeof( len ) , 1, Big_EndianType ) )
		return false;
		
	m_dpi	= DPI();
	while( len > 0 )
	{
		char	header[ 5 ];
		if( 4 != reader->Read( header , sizeof( char ) , 4 , Big_EndianType ) )
			return false;
		header[ 4 ]='\0';

		// check 8BIM
		if( strcmp( header , "8BIM" ) != 0 )
			return false;
		
		// id
		unsigned short	id;
		if( 1 != reader->Read( &id , sizeof( id ) , 1 , Big_EndianType ) )
			return false;

		//PString
		unsigned char	strlen;
		if( 1 != reader->Read( &strlen , sizeof( strlen ) , 1 , Big_EndianType ) )
			return false;
		strlen |= 1;
		if( false == reader->Seek( reader->GetPosition() + strlen ) )
			return false;
		
		// datasize
		unsigned long	size;
		if( 1 != reader->Read( &size , sizeof( size ) , 1 , Big_EndianType ) )
			return false;
		
		// data
		if( id == 0x03ED && size >= 16 )
		{
			unsigned long	hresfixed;
			if( 1 != reader->Read( &hresfixed , sizeof( hresfixed ) , 1 , Big_EndianType ) )
				return false;
			unsigned short	hresunit;
			if( 1 != reader->Read( &hresunit , sizeof( hresunit ) , 1 , Big_EndianType ) )
				return false;
			unsigned short	widthunit;
			if( 1 != reader->Read( &widthunit , sizeof( widthunit ) , 1 , Big_EndianType ) )
				return false;
			unsigned long	vresfixed;
			if( 1 != reader->Read( &vresfixed , sizeof( vresfixed ) , 1 , Big_EndianType ) )
				return false;
			unsigned short	vresunit;
			if( 1 != reader->Read( &vresunit , sizeof( vresunit ) , 1 , Big_EndianType ) )
				return false;
			unsigned short	heightunit;
			if( 1 != reader->Read( &heightunit , sizeof( heightunit ) , 1 , Big_EndianType ) )
				return false;
			if( false == reader->Seek( reader->GetPosition() + ( size - 16 ) ) )
				return false;
			m_dpi	= DPI( hresfixed / 65536.0f , vresfixed / 65536.0f );
		}
		else 
		{
			if( false == reader->Seek( reader->GetPosition() + size ) )
				return false;
		}
		len	= len - 4 - 2 - 1 - strlen - 4 - size;
	}
	return true;
}
//=================================================================================================
//!	layer and mask section
//!	@retval			---
//-------------------------------------------------------------------------------------------------
bool LayerAndMaskSection
		(
		iFileStreamRead&	reader
		)
{
	// get length of the section
	unsigned long	len;
	if( 1 != reader->Read( &len , sizeof( len ) ,1, Big_EndianType ) )
		return false;
	
	// seek read pointer
	if( false == reader->Seek( reader->GetPosition() + len ) )
		return false;
	return true;
}
//=================================================================================================
//!	image data section.
//!	@retval			---
//-------------------------------------------------------------------------------------------------
bool ImageDataSection
		(
		iFileStreamRead&	reader
		)
{
	// read compress flag.
	short	comp;
	{
		if( 1 != reader->Read( &comp , sizeof( comp ) , 1 , Big_EndianType ) )
			return false;
	}
	// read data.
	if ( comp == 0 )
	{
		if( false == UnCompImageData( reader ) )
			return false;
	}	
	else
	{
		if( false == CompImageData( reader ) )
			return false;
	}	
	return true;
}
//=================================================================================================
//!	read uncompress image data.
//!	@retval			---
//-------------------------------------------------------------------------------------------------
bool UnCompImageData
		(
		iFileStreamRead&	reader
		)
{
	// check mode and channel bit.
	if ( m_depth != 8 )
		return false;
	if ( m_mode != 3 )
		return false;

	// set channnel buf.
	m_image.Resize( m_channel );
	
	// read each channels.
	int		channeloff;
	for ( channeloff = 0 ; channeloff < m_channel ; channeloff++ )
	{
		m_image[ channeloff ].Resize( m_size.width * m_size.height );
		
		int	len = m_size.width * m_size.height;
		if( len != reader->Read( m_image[ channeloff ].GetPtr() , sizeof( uint8 ) , len , Big_EndianType ) )
			return false;
	}
	return true;
}
//=================================================================================================
//!	read compress image data.
//!	@retval			---
//-------------------------------------------------------------------------------------------------
bool CompImageData
		(
		iFileStreamRead&	reader
		)
{
	// check mode and channel bit.
	if ( m_depth != 8 )
		return false;
	if ( m_mode != 3 )
		return false;

	// set channel buffer.
	m_image.Resize( m_channel );
	
	//get each channel's byte.
	Straightdata<Array<uint16>>		bytelen;
	{	
		bytelen.Resize( m_channel );
		int		channeloff;
		int		lineoff;
		for ( channeloff = 0 ; channeloff < m_channel ; channeloff++ )
		{
			bytelen[ channeloff ].Resize( m_size.height );
			
			for ( lineoff = 0 ; lineoff < m_size.height ; lineoff++ )
			{
				unsigned short	len;
				if( 1 != reader->Read( &len, sizeof( len ) , 1, Big_EndianType ) )
					return false;
				bytelen[ channeloff ][ lineoff ]	= len;	
			}
		}	
	}
	// read each channel.
	int		channeloff;
	for ( channeloff = 0 ; channeloff < m_channel ; channeloff++ )
	{
		// set channel size.
		m_image[ channeloff ].Resize( m_size.width * m_size.height );
		int		dataoff	= 0;
		int		lineoff;		
		for ( lineoff = 0 ; lineoff < m_size.height ; lineoff++ )
		{
			// read data.
			int		off = 0;
			while ( off < bytelen[ channeloff ][ lineoff ] )
			{
				char	num = 0;
				int		i;
				
				// get number of code.
				if( 1 != reader->Read( &num , sizeof( num ) , 1 , Big_EndianType ) )
					return false;
				off++;
				int		t_num	= num;	

				// in case of 0~127 , copy ( num + 1 ) byte.
				if ( ( 0 <= t_num ) && ( t_num <= 127 ) )
				{
					t_num	= num + 1;
					for ( i = 0 ; i < t_num ; i++ )
					{
						unsigned char	bit	= 0;	
						if( 1 != reader->Read( &bit , sizeof( bit ) , 1 , Big_EndianType ) )
							return false;
						off++;
						m_image[ channeloff ][ dataoff ]	= bit;
						dataoff++;
					}

				}
				else if ( t_num == ( int )( unsigned int )-128 )
					continue;
				// incase of  -1~-127 , copy ( -num + 1 ) byte
				else
				{
					t_num	= -t_num + 1;
					
					// get code.
					unsigned char	bit;	
					if( 1 != reader->Read( &bit , sizeof( bit ) , 1,  Big_EndianType ) )
						return false;
					off++;
					// set channel data.
					for ( i = 0 ; i < t_num ; i++ )
					{
						m_image[ channeloff ][ dataoff ]	= bit;
						dataoff++;
					}
				}
			}
		}
	}
	return true;
}

// "ILoaderImagefile" interface functions
public:
//=================================================================================================
//!	check image format
//!	@retval			---
//-------------------------------------------------------------------------------------------------
bool cb_call CheckImagefileSign
		(
		iFileStreamRead&	reader
		)
{
	int64	save	= reader->GetPosition();

	// get header.
	char	header[ 5 ];
	if( 4 != reader->Read( header , sizeof( char ) , 4 , Big_EndianType ) )
	{
		reader->Seek( save );
		return false;
	}
	header[ 4 ]='\0';

	// check NTP file.
	if( strcmp( header , "8BPS" ) != 0 )
	{
		reader->Seek( save );
		return false;
	}

	reader->Seek( save );
	return true;
}
//=================================================================================================
//!	load
//!	@retval			---
//-------------------------------------------------------------------------------------------------
bool cb_call Load
		(
		iFileStreamRead&	reader
		)
{
	// save file pointer.
	int64	save	= reader->GetPosition();
	
	// free
	Free();
	
	// read file header section.
	if( false == FileHeaderSection( reader ) )
	{
		Free();
		reader->Seek( save );
		return false;
	}
	// read color mode section.
	if( false == ColorModeSection( reader ) )
	{
		Free();
		reader->Seek( save );
		return false;
	}
	// read image resource section.
	if ( false == ImageResourceSection( reader ) )
	{
		Free();
		reader->Seek( save );
		return false;
	}
	// read layer and mask section.
	if ( false == LayerAndMaskSection( reader ) )
	{
		Free();
		reader->Seek( save );
		return false;
	}

	// read image data section.
	if( false == ImageDataSection( reader ) )
	{
		Free();
		reader->Seek( save );
		return false;
	}
	//check data.
	if( m_image.GetDatanum() < 3 )
	{
		Free();
		reader->Seek( save );
		return false;
	}
	return true;
}
//=================================================================================================
//!	get image num.
//!	@retval			---
//-------------------------------------------------------------------------------------------------
int32 cb_call GetImageNum()
{
	if( m_image.GetDatanum() == 0 )
		return 0;
	return 1;
}
//=================================================================================================
//!	get dpi
//!	@retval			---
//-------------------------------------------------------------------------------------------------
DPI cb_call GetImageDPI
		(
		int32		imageoff
		)const
{
	return m_dpi;
}
//=================================================================================================
//!	get image size.
//!	@retval			---
//-------------------------------------------------------------------------------------------------
isize cb_call GetImageSize
		(
		int32		imageoff
		)
{
	if( imageoff != 0 )
		return isize();
	return m_size;
}
//=================================================================================================
//!	get image.
//!	@retval			---
//-------------------------------------------------------------------------------------------------
bool cb_call GetImage
		(
		int32		imageoff , 
		pixelformat	destformat , 
		void*		t_dest , 
		int			pitchbyte
		)
{
	if( m_image.GetDatanum() == 0 )
		return false;
	if( imageoff != 0 )
		return false;
		
//	int		off , num = m_size.width * m_size.height;
	uint8*	dest = ( uint8* )t_dest;
	
	if( m_image.GetDatanum() >= 4 )
	{
		uint8	*pr	= m_image[ 0 ].GetPtr();
		uint8	*pg	= m_image[ 1 ].GetPtr();
		uint8	*pb	= m_image[ 2 ].GetPtr();
		uint8	*pa	= m_image[ 3 ].GetPtr();

		int		y;
		for( y = 0 ; y < m_size.height ; y++ )
		{
			int		x;
			uint8	*p	= dest;
			for( x = 0 ; x < m_size.width ; x++ )
			{
				rgba	pix;
				pix.r	= *pr;
				pix.g	= *pg;
				pix.b	= *pb;
				pix.a	= *pa;

				to_pixel( destformat , p , pix );

				p += get_pixel_byte( destformat );
				pr++;
				pg++;
				pb++;
				pa++;
			}
			dest	+= pitchbyte;
		}
	}
	else
	{
		uint8	*pr	= m_image[ 0 ].GetPtr();
		uint8	*pg	= m_image[ 1 ].GetPtr();
		uint8	*pb	= m_image[ 2 ].GetPtr();

		int		y;
		for( y = 0 ; y < m_size.height ; y++ )
		{
			int		x;
			uint8	*p	= dest;
			for( x = 0 ; x < m_size.width ; x++ )
			{
				rgba	pix;
				pix.r	= *pr;
				pix.g	= *pg;
				pix.b	= *pb;
				pix.a	= 255;

				to_pixel( destformat , p , pix );

				p += get_pixel_byte( destformat );
				pr++;
				pg++;
				pb++;
			}
			dest	+= pitchbyte;
		}
	}
	return true;
}


// protect functions
protected:
// public functions
public:
//=================================================================================================
//!	construct
//-------------------------------------------------------------------------------------------------
LoaderImagefile_psd() : 
		m_version( 0 ) , 
		m_channel( 0 ) , 
		m_mode( 0 ) , 
		m_depth( 0 )
{
}
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// global variable define

///////////////////////////////////////////////////////////////////////////////////////////////////
// global functions define

};	//namespace

//using namespace icubic;		

#pragma pack( pop )			//release align
