#include"CPNGLoader.h"

#include"../../../Auxiliary/FileIO/CFileRead.h"
#include"../../../Auxiliary/FileIO/CFileWrite.h"
#include"../../../Auxiliary/Debug/CAssert.h"
#include"../../../Auxiliary/Debug/CException.h"

#include<png.h>


//!	PNG 読み込み用構造体
struct TPngFileBuffer
{
	Maid::CFileRead*		pFile;
};

static void PngReadFunc( png_struct *Png, png_bytep buf, png_size_t size )
{
    TPngFileBuffer* PngFileBuffer = (TPngFileBuffer*)png_get_io_ptr(Png);

	PngFileBuffer->pFile->Read( buf, (int)size );
}

namespace Maid
{
	namespace CPNGLoader
	{

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! PNGファイルをメモリ上に読み込む
/*!
	@param	FileName	[i ]	ファイル名
	@param	dst			[i ]	転送先
 */
void   Load( const mstring& FileName, CTextureBufferMemory& dst )
{
	png_struct*	pStruct = NULL;
	png_info*	pInfo   = NULL;

	pStruct = ::png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
	pInfo	= ::png_create_info_struct(pStruct);


	CFileRead hFile;
	hFile.Open( FileName );

	TPngFileBuffer	buf;
	buf.pFile = &hFile;

	::png_set_read_fn( pStruct, (png_voidp)&buf, (png_rw_ptr)PngReadFunc );

	::png_read_info( pStruct, pInfo );
	png_uint_32 Width,Height;
	int BitDepth,ColorType;
	int InterlaceType;
	int CompressionType;
	int FilterType;
	::png_get_IHDR(pStruct,pInfo,&Width,&Height,&BitDepth,&ColorType,&InterlaceType,&CompressionType,&FilterType);

	if( BitDepth!=8 )						{ MAID_THROWEXCEPTION( MAIDTEXT("BitDepth!=8") ); }//	知らん(^^; <- これ１カラー（赤など）のビット数です
	if( InterlaceType!=PNG_INTERLACE_NONE ) { MAID_THROWEXCEPTION( MAIDTEXT("!PNG_INTERLACE_NONE") ); }//	インターレースも知らん

	const bool UsePalette = IsFlag(ColorType,0x01);
	const bool UseColor   = IsFlag(ColorType,0x02);
	const bool UseAlpha   = IsFlag(ColorType,0x04);

	if( !UseColor ) { MAID_THROWEXCEPTION( MAIDTEXT("!UseColor") ); }	//	グレースケールなんて知らん

	PIXELFORMAT fmt;

//	if( UsePalette ){ fmt = PIXELFORMAT_P08X08R08G08B08I; }	//	パレットがあるなら８ＢＩＴ
	if( UsePalette ){ MAID_ASSERT( true, "でも未対応" );  }
	ef( UseAlpha   ){ fmt = PIXELFORMAT_A08R08G08B08I;	  }	//	パレット無し＋アルファありなら３２ＢＩＴ
	else			{ fmt = PIXELFORMAT_R08G08B08I;		  }	//	それ以外は２４ＢＩＴ


	{
		::png_set_bgr( pStruct );

		png_colorp Palette;
		int PaletteCount=0;
		if( UsePalette ) { png_get_PLTE( pStruct, pInfo, &Palette, &PaletteCount ); }

		const int PaletteSize = GetCLUTBPP(fmt)/8 * GetCLUTLength(fmt);
		const int ImagePitch  = Width * GetPixelBPP(fmt) /8;
		const int ImageSize   = ImagePitch*Height;

		dst.Create( SIZE2DI(Width,Height), fmt);
/*
		{
			uint08*	pNow = m_pMemory.get();

			if( UsePalette )
			{	//	ついでにパレット変換も行う（今は未実装）
				m_pPalette = (PALETTEENTRY*)pNow; 
				m_pImage   = pNow + PaletteSize;
			}
			else
			{
				m_pPalette = NULL; 
				m_pImage   = pNow;
			}
		}
*/
		const int number_passes = png_set_interlace_handling(pStruct);

		MySTL::vector<png_bytep> LineHeadPtr(Height);

		unt08*	  pNowLine = (unt08*)dst.GetSurfaceEx(0).GetPlanePTR();
		for( int y=0; y<(int)LineHeadPtr.size(); ++y )
		{
			LineHeadPtr[y] = pNowLine + ImagePitch*y;
		}

		for (int pass = 0; pass < number_passes; ++pass )
		{
			for (png_uint_32 y = 0; y < Height; ++y)
			{
				png_read_rows(pStruct, &LineHeadPtr[y], png_bytepp_NULL, 1);
			}
		}

		::png_destroy_read_struct( &pStruct, &pInfo, NULL );
	}
}

#if 0

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! PNGファイルをメモリ上に読み込む
/*!
 *	引数などは IImageFile::Load を参照すること
 */
MRESULT CImageFilePNG::Load( const MySTL::vector<uint08>& FileImage )
{
	Destory();

	png_struct*	pStruct = NULL;
	png_info*	pInfo   = NULL;

	pStruct = ::png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
	pInfo	= ::png_create_info_struct(pStruct);

	const MySTL::vector<uint08>& FileData = FileImage;

	TPngFileBuffer	buf;
	buf.Buffer = &(FileData[0]);
	buf.Pos    = 0;

	::png_set_read_fn( pStruct, (png_voidp)&buf, (png_rw_ptr)PngReadFunc );

	::png_read_info( pStruct, pInfo );
	png_uint_32 Width,Height;
	int BitDepth,ColorType;
	int InterlaceType;
	int CompressionType;
	int FilterType;
	::png_get_IHDR(pStruct,pInfo,&Width,&Height,&BitDepth,&ColorType,&InterlaceType,&CompressionType,&FilterType);

	if( BitDepth!=8 )						{ return 2; }//	知らん(^^; <- これ１カラー（赤など）のビット数です
	if( InterlaceType!=PNG_INTERLACE_NONE ) { return 3; }//	インターレースも知らん

	const bool UsePalette = IsFlag(ColorType,0x01);
	const bool UseColor   = IsFlag(ColorType,0x02);
	const bool UseAlpha   = IsFlag(ColorType,0x04);

	if( !UseColor ) { return 4; }	//	グレースケールなんて知らん

	PIXELFORMAT fmt;
	uint08 BPP;

	if( UsePalette ){ fmt = PIXELFORMAT_P8X8R8G8B8; BPP =  8; }	//	パレットがあるなら８ＢＩＴ
	ef( UseAlpha   ){ fmt = PIXELFORMAT_A8R8G8B8;	BPP = 32; }	//	パレット無し＋アルファありなら３２ＢＩＴ
	else			{ fmt = PIXELFORMAT_R8G8B8;		BPP = 24; }	//	それ以外は２４ＢＩＴ


	{
		::png_set_bgr( pStruct );

		png_colorp Palette;
		int PaletteCount=0;
		if( UsePalette ) { png_get_PLTE( pStruct, pInfo, &Palette, &PaletteCount ); }

		const uint32 PaletteSize = UsePalette? 4*256 : 0;
		const uint32 ImagePitch  = Width*BPP/8;
		const uint32 ImageSize   = ImagePitch*Height;

		m_pMemory.reset( new uint08[PaletteSize+ImageSize] );

		{
			uint08*	pNow = m_pMemory.get();

			if( UsePalette )
			{	//	ついでにパレット変換も行う（今は未実装）
				m_pPalette = (PALETTEENTRY*)pNow; 
				m_pImage   = pNow + PaletteSize;
			}
			else
			{
				m_pPalette = NULL; 
				m_pImage   = pNow;
			}
		}

		const int number_passes = png_set_interlace_handling(pStruct);

		MySTL::vector<png_bytep> LineHeadPtr(Height);

		uint08*	  pNowLine = (uint08*)m_pImage;
		for( int y=0; y<(int)LineHeadPtr.size(); ++y )
		{
			LineHeadPtr[y] = pNowLine + ImagePitch*y;
		}

		for (int pass = 0; pass < number_passes; ++pass )
		{
			for (png_uint_32 y = 0; y < Height; ++y)
			{
				png_read_rows(pStruct, &LineHeadPtr[y], png_bytepp_NULL, 1);
			}
		}

		::png_destroy_read_struct( &pStruct, &pInfo, NULL );
	}


	m_Width  = Width;
	m_Height = Height;
	m_Format = fmt;

	return MRESULT_OK;
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 読み込んであるイメージをpngとして保存する
/*!
 *	引数などは IImageFile::Save を参照すること
 */
MRESULT CImageFilePNG::Save( const MySTL::wstring& FileName )
{
	DEN_ASSERT( !m_pMemory, "まだ作成されていません" );

	CFileWrite hFile;

	hFile.Open( FileName, CFileWrite::OPENOPTION_NEW );

	png_structp png_ptr;
	png_infop info_ptr;

	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if( png_ptr==NULL ) { return 0; }
	info_ptr = png_create_info_struct(png_ptr);
	if(info_ptr==NULL)
	{
		png_destroy_write_struct(&png_ptr, png_infopp_NULL);
		return 0;
	}

	const uint32 Width  = GetWidth();
	const uint32 Height = GetHeight();

    png_init_io(png_ptr, hFile.GetHandle() );

	{
		png_color_8 sig_bit;

		switch( m_Format )
		{
		case PIXELFORMAT_A8R8G8B8:
		case PIXELFORMAT_X8R8G8B8:
			{
				sig_bit.red   = 8;
				sig_bit.green = 8;
				sig_bit.blue  = 8;
				sig_bit.alpha = 8;
				png_set_IHDR(png_ptr, info_ptr, Width, Height, 8, PNG_COLOR_TYPE_RGB_ALPHA,PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_BASE);
			}break;
 		case PIXELFORMAT_R8G8B8:
			{
				sig_bit.red   = 8;
				sig_bit.green = 8;
				sig_bit.blue  = 8;
				sig_bit.alpha = 0;
				png_set_IHDR(png_ptr, info_ptr, Width, Height, 8, PNG_COLOR_TYPE_RGB,PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_BASE);

			}break;
		default:{ DEN_ASSERT( true, "対応していません %d", m_Format ); }break;
		}
		png_set_sBIT(png_ptr, info_ptr, &sig_bit);
	}
	{
		png_write_info(png_ptr, info_ptr);
		png_set_bgr(png_ptr);
	}
	{
		MySTL::vector<png_bytep> row_pointers(Height);
	 
		const uint32 Pitch = m_Width*GetPixelBPP(m_Format)/8;
		for (uint32 i = 0; i<Height; ++i )
		{
			row_pointers[i] = (png_bytep)((uint08*)m_pImage + Pitch*i);
		}
		png_write_image(png_ptr, &(row_pointers[0]));
		png_write_end(png_ptr, info_ptr);
	}
	::png_destroy_info_struct( png_ptr, &info_ptr);

	::png_destroy_write_struct( &png_ptr, &info_ptr );

	return MRESULT_OK;
}

#endif

	}
}