#pragma once

#include "../../lm/math_util.h"
#include "../RawImage.h"
#include "../Color.h"
#include "GdiBmpIO.h"


namespace lgr
{

//! GDI+を利用した画像ファイル入出力クラス.
class GdiplusImageIO
{
public:
	template<typename T>
	static bool LoadFile( const WCHAR* filename , RawImage< lgr::color3<T> >& o_image );
	template<typename T>
	static bool LoadFile( const WCHAR* filename , RawImage< lgr::color4<T> >& o_image );

	template<typename T>
	static bool SaveFile( const WCHAR* filename , const GdiBmpIO::ImageFormat& image_format , const RawImage< lgr::color3<T> >& image );
	template<typename T>
	static bool SaveFile( const WCHAR* filename , const GdiBmpIO::ImageFormat& image_format , const RawImage< lgr::color4<T> >& image );

private:
	template<typename T>
	static void ColorConvertFromGDIPlus( const Gdiplus::Color& color_src , color3<T>& color_dst );
	template<typename T>
	static void ColorConvertFromGDIPlus( const Gdiplus::Color& color_src , color4<T>& color_dst );

	template<typename T>
	static void ColorElemConvertFromGDIPlus( const BYTE& elem_src , T&      elem_dst ){ elem_dst = (T)elem_src; }
	static void ColorElemConvertFromGDIPlus( const BYTE& elem_src , float&  elem_dst ){ elem_dst = (float)elem_src / 255.0f; }
	static void ColorElemConvertFromGDIPlus( const BYTE& elem_src , double& elem_dst ){ elem_dst = (double)elem_src / 255.0; }


	template<typename T>
	static void ColorConvertToGDIPlus( const color3<T>& color_src , Gdiplus::Color& color_dst );
	template<typename T>
	static void ColorConvertToGDIPlus( const color4<T>& color_src , Gdiplus::Color& color_dst );

	template<typename T>
	static void ColorElemConvertToGDIPlus( const T&      elem_src , BYTE& elem_dst ){ elem_dst = (BYTE)( lm::clamp( T(0) , elem_src , T(255) ) ); }
	static void ColorElemConvertToGDIPlus( const float&  elem_src , BYTE& elem_dst ){ elem_dst = (BYTE)( lm::clamp( 0.0f , elem_src , 1.0f ) * 255.0f ); }
	static void ColorElemConvertToGDIPlus( const double& elem_src , BYTE& elem_dst ){ elem_dst = (BYTE)( lm::clamp( 0.0 , elem_src , 1.0 ) * 255.0 ); }
};



// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template<typename T>
bool GdiplusImageIO::LoadFile( const WCHAR* filename , RawImage< lgr::color3<T> >& o_image )
{
	Gdiplus::Bitmap bmp( filename );
	if( bmp.GetLastStatus() != Gdiplus::Ok )
		return false;

	o_image.resize( bmp.GetWidth() , bmp.GetHeight() );
	for( UINT x = 0 ; x < bmp.GetWidth() ; ++x )
	{
		for( UINT y = 0 ; y < bmp.GetHeight() ; ++y )
		{
			Gdiplus::Color c_src;
			bmp.GetPixel( x , y , &c_src );

			ColorConvertFromGDIPlus( c_src , o_image.at(x,y) );
		}
	}

	return true;
}

template<typename T>
bool GdiplusImageIO::LoadFile( const WCHAR* filename , RawImage< lgr::color4<T> >& o_image )
{
	Gdiplus::Bitmap bmp( filename );
	if( bmp.GetLastStatus() != Gdiplus::Ok )
		return false;

	o_image.resize( bmp.GetWidth() , bmp.GetHeight() );
	for( UINT x = 0 ; x < bmp.GetWidth() ; ++x )
	{
		for( UINT y = 0 ; y < bmp.GetHeight() ; ++y )
		{
			Gdiplus::Color c_src;
			bmp.GetPixel( x , y , &c_src );

			ColorConvertFromGDIPlus( c_src , o_image.at(x,y) );
		}
	}

	return true;
}


template<typename T>
bool GdiplusImageIO::SaveFile( const WCHAR* filename , const GdiBmpIO::ImageFormat& image_format , const RawImage< lgr::color3<T> >& image )
{
	Gdiplus::Bitmap bmp( image.size_x() , image.size_y() );
	for( size_t x = 0 ; x < image.size_x() ; ++x )
	{
		for( size_t y = 0 ; y < image.size_y() ; ++y )
		{
			Gdiplus::Color c_dst;
			ColorConvertToGDIPlus( image.at(x,y) , c_dst );

			bmp.SetPixel( x , y , c_dst );
		}
	}

	return GdiBmpIO::Save(bmp, filename , image_format );
}

template<typename T>
bool GdiplusImageIO::SaveFile( const WCHAR* filename , const GdiBmpIO::ImageFormat& image_format , const RawImage< lgr::color4<T> >& image )
{
	Gdiplus::Bitmap bmp( image.size_x() , image.size_y() );
	for( size_t x = 0 ; x < image.size_x() ; ++x )
	{
		for( size_t y = 0 ; y < image.size_y() ; ++y )
		{
			Gdiplus::Color c_dst;
			ColorConvertToGDIPlus( image.at(x,y) , c_dst );

			bmp.SetPixel( x , y , c_dst );
		}
	}

	return GdiBmpIO::Save(bmp, filename , image_format );
}


template<typename T>
void GdiplusImageIO::ColorConvertFromGDIPlus( const Gdiplus::Color& color_src , color3<T>& color_dst )
{
	ColorElemConvertFromGDIPlus( color_src.GetR() , color_dst.r() );
	ColorElemConvertFromGDIPlus( color_src.GetG() , color_dst.g() );
	ColorElemConvertFromGDIPlus( color_src.GetB() , color_dst.b() );
}

template<typename T>
void GdiplusImageIO::ColorConvertFromGDIPlus( const Gdiplus::Color& color_src , color4<T>& color_dst )
{
	ColorElemConvertFromGDIPlus( color_src.GetR() , color_dst.r() );
	ColorElemConvertFromGDIPlus( color_src.GetG() , color_dst.g() );
	ColorElemConvertFromGDIPlus( color_src.GetB() , color_dst.b() );
	ColorElemConvertFromGDIPlus( color_src.GetA() , color_dst.a() );
}

template<typename T>
void GdiplusImageIO::ColorConvertToGDIPlus( const color3<T>& color_src , Gdiplus::Color& color_dst )
{
	BYTE r , g , b;
	ColorElemConvertToGDIPlus( color_src.r() , r );
	ColorElemConvertToGDIPlus( color_src.g() , g );
	ColorElemConvertToGDIPlus( color_src.b() , b );
	color_dst = Gdiplus::Color( r , g , b );
}

template<typename T>
void GdiplusImageIO::ColorConvertToGDIPlus( const color4<T>& color_src , Gdiplus::Color& color_dst )
{
	BYTE r , g , b , a;
	ColorElemConvertToGDIPlus( color_src.r() , r );
	ColorElemConvertToGDIPlus( color_src.g() , g );
	ColorElemConvertToGDIPlus( color_src.b() , b );
	ColorElemConvertToGDIPlus( color_src.a() , a );
	color_dst = Gdiplus::Color( a , r , g , b );
}


}
