#pragma once

#include "Color.h"
#include "RawImage.h"


namespace lib_graph
{


//! OpenCVの画像形式とRawImage形式を変換するクラス.
class OpenCVImageConverter
{
public:
	template<typename T>
	static void ConvertImageFromCV( const IplImage* cv_image , RawImage< color3<T> >& o_image );

	template<typename T>
	static void ConvertImageFromCV( const IplImage* cv_image , RawImage< color4<T> >& o_image );

	template<typename T>
	static bool ConvertToAlphaMaskFromCV( const IplImage* cv_image , RawImage< color4<T> >& o_image );


protected:
	static int GetPixelHeadIndex( const IplImage* cv_image , int pixel_x , int pixel_y );

	// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// opencv -> rawimage
	template<typename T>
	static void ConvertPixelFromCV( const IplImage* cv_image , int pixel_x , int pixel_y , RawImage< color3<T> >& o_image );
	template<typename T>
	static void ConvertPixelFromCV( const IplImage* cv_image , int pixel_x , int pixel_y , RawImage< color4<T> >& o_image );

	static void ConvertChannelFromCV( float         & o_channel , const char i_channel );
	static void ConvertChannelFromCV( double        & o_channel , const char i_channel );
	static void ConvertChannelFromCV( char          & o_channel , const char i_channel );
	static void ConvertChannelFromCV( unsigned char & o_channel , const char i_channel );
};



inline int OpenCVImageConverter::GetPixelHeadIndex( const IplImage* cv_image , int pixel_x , int pixel_y )
{
	return cv_image->widthStep * pixel_y + cv_image->nChannels * pixel_x;
}


//! OpenCVの画像形式(IplImage)をRawImage3fに変換する
template<typename T> inline
void OpenCVImageConverter::ConvertImageFromCV( const IplImage* cv_image , RawImage< color3<T> >& o_image )
{
	o_image.resize( cv_image->width , cv_image->height );

	for( int i = 0 ; i < cv_image->width ; ++i )
	{
		for( int j = 0 ; j < cv_image->height ; ++j )
		{
			ConvertPixelFromCV( cv_image , i , j , o_image );
		}
	}
}

//! OpenCVの画像形式(IplImage)をRawImage4fに変換する
template<typename T> inline
void OpenCVImageConverter::ConvertImageFromCV( const IplImage* cv_image , RawImage< color4<T> >& o_image )
{
	o_image.resize( cv_image->width , cv_image->height );

	for( int i = 0 ; i < cv_image->width ; ++i )
	{
		for( int j = 0 ; j < cv_image->height ; ++j )
		{
			ConvertPixelFromCV( cv_image , i , j , o_image );
		}
	}
}

//! OpenCVの画像形式(IplImage)の輝度をRawImage4fのアルファ成分に読み込む.
//! 画像のサイズが一致しない場合などに失敗する.
template<typename T>
bool OpenCVImageConverter::ConvertToAlphaMaskFromCV( const IplImage* cv_image , RawImage< color4<T> >& o_image )
{
	_ASSERTE( cv_image != NULL );
	if( cv_image == NULL )
		return false;

	// サイズが一致していなければリサイズ.
	IplImage* resized_image = NULL;
	if( ( (size_t)cv_image->width != o_image.size_x() ) || ( (size_t)cv_image->height != o_image.size_y() ) )
	{
		resized_image = cvCreateImage( cvSize( o_image.size_x() , o_image.size_y() ) , cv_image->depth , cv_image->nChannels );
		cvResize( cv_image , resized_image , CV_INTER_CUBIC );
	}

	const IplImage* alpha_mask;
	if( resized_image != NULL )
		alpha_mask = resized_image;
	else
		alpha_mask = cv_image;

	for( int i = 0 ; i < alpha_mask->width ; ++i )
	{
		for( int j = 0 ; j < alpha_mask->height ; ++j )
		{
			int pixel_head = GetPixelHeadIndex( alpha_mask , i , j );

			ConvertChannelFromCV( o_image.at( i , j ).a() , alpha_mask->imageData[ pixel_head + 0 ] );
		}
	}

	if( resized_image != NULL )
		cvReleaseImage( &resized_image );

	return true;
}


template<typename T> inline
void OpenCVImageConverter::ConvertPixelFromCV( const IplImage* cv_image , int pixel_x , int pixel_y , RawImage< color3<T> >& o_image )
{
	int pixel_head = GetPixelHeadIndex( cv_image , pixel_x , pixel_y );

	ConvertChannelFromCV( o_image.at( pixel_x , pixel_y ).b() , cv_image->imageData[ pixel_head + 0 ] );
	ConvertChannelFromCV( o_image.at( pixel_x , pixel_y ).g() , cv_image->imageData[ pixel_head + 1 ] );
	ConvertChannelFromCV( o_image.at( pixel_x , pixel_y ).r() , cv_image->imageData[ pixel_head + 2 ] );
}

template<typename T> inline
void OpenCVImageConverter::ConvertPixelFromCV( const IplImage* cv_image , int pixel_x , int pixel_y , RawImage< color4<T> >& o_image )
{
	int pixel_head = GetPixelHeadIndex( cv_image , pixel_x , pixel_y );

	ConvertChannelFromCV( o_image.at( pixel_x , pixel_y ).b() , cv_image->imageData[ pixel_head + 0 ] );
	ConvertChannelFromCV( o_image.at( pixel_x , pixel_y ).g() , cv_image->imageData[ pixel_head + 1 ] );
	ConvertChannelFromCV( o_image.at( pixel_x , pixel_y ).r() , cv_image->imageData[ pixel_head + 2 ] );
	unsigned char color_max = 255;
	ConvertChannelFromCV( o_image.at( pixel_x , pixel_y ).a() , reinterpret_cast<const char&>(color_max) );
}


inline void OpenCVImageConverter::ConvertChannelFromCV( float& o_channel , const char i_channel )
{
	const unsigned char& channel_ub = reinterpret_cast<const unsigned char&>(i_channel);
	o_channel = (float)channel_ub / 255.0f;
}

inline void ConvertChannelFromCV( double& o_channel , const char i_channel )
{
	const unsigned char& channel_ub = reinterpret_cast<const unsigned char&>(i_channel);
	o_channel = (double)channel_ub / 255;
}

inline void OpenCVImageConverter::ConvertChannelFromCV( char& o_channel , const char i_channel )
{
	o_channel = i_channel;
}

inline void OpenCVImageConverter::ConvertChannelFromCV( unsigned char& o_channel , const char i_channel )
{
	o_channel = (unsigned char)i_channel;
}


}
