#include <windows.h>
#include <crtdbg.h>

#include <fstream>

#include "CBitMap.h"

using namespace std;

// メモリからクラスを作成する
CBitMap::CBitMap( const BITMAPINFO *bmpinfo, const unsigned char *image, const int imagesize)
	: m_ImageData( NULL), m_ImageSize( 0), m_Palette( 0), m_PaletteSize( 0),
	m_ColorBit( 0), m_Width( 0), m_Height( 0), m_Pitch( 0)
{
	const BITMAPINFOHEADER	*BitmapInfo = &( bmpinfo->bmiHeader);
	// 圧縮フォーマットのチェック
	// 圧縮は対応しない
	if ( BI_RGB != BitmapInfo->biCompression && BI_BITFIELDS != BitmapInfo->biCompression)
		return;

	// パレットのサイズを計算
	int PaletteSize;
	if ( BitmapInfo->biClrUsed == 0)
	{
		switch ( BitmapInfo->biBitCount)
		{
		case  1: PaletteSize = sizeof(RGBQUAD) * 2  ;break;
		case  4: PaletteSize = sizeof(RGBQUAD) * 16 ;break;
		case  8: PaletteSize = sizeof(RGBQUAD) * 256;break;
		case 24: PaletteSize = 0;break;
		case 16:
		case 32: PaletteSize = BitmapInfo->biCompression == BI_RGB ? 0: sizeof( DWORD) * 3;
		}
	}
	else
		PaletteSize = BitmapInfo->biClrUsed * sizeof( RGBQUAD);

	int ColorBit = BitmapInfo->biBitCount;
	int Width = BitmapInfo->biWidth;
	int Height = BitmapInfo->biHeight;

	// パレットサイズがゼロでないなら
	if ( 0 != PaletteSize)
	{
		_ASSERT( 24 != ColorBit);
		// パレットの確保
		m_Palette = new unsigned char [ PaletteSize];
		memcpy( m_Palette, reinterpret_cast< const unsigned char *>( bmpinfo) + sizeof( BITMAPINFOHEADER), m_PaletteSize);
	}
	// イメージのコピー
	m_ImageData = new unsigned char [ imagesize];
	memcpy( m_ImageData, image, imagesize);

	// 各値の設定
	m_ImageSize = imagesize;			// サイズ
	m_PaletteSize =	PaletteSize;
	m_ColorBit = ColorBit;		// カラービット数
	m_Width = Width;		// 幅
	m_Height = Height;		// 高さ
	m_Pitch = CalcPitch( m_Width, m_Height, m_ColorBit);		// ピッチ
}


//指定されたビットマップをメモリに読みこむ
bool CBitMap::LoadBitmapFile( const string &filename)
{
	// 初期化
	DeleteBuffer( m_ImageData);
	DeleteBuffer( m_Palette);
	m_ImageSize = 0;			// サイズ
	m_PaletteSize = 0;
	m_ColorBit = 0;		// カラービット数
	m_Width = 0;		// 幅
	m_Height = 0;		// 高さ
	m_Pitch = 0;		// ピッチ

	//ファイルを開く
	ifstream file( filename.c_str(), ios::in | ios::binary);
	if ( !file.is_open())
		return false;
	//ファイルサイズを調べる
	file.seekg( 0, ios::end);
	//エラーだったらばfalseを返す
	if ( !file.good())
		return false;
	int DataSize = file.tellg();
	if ( !file.good())
		return false;

	//ファイルの先頭に戻す
	file.seekg( 0, ios::beg);
	if ( !file.good())
		return false;

	//ビットマップファイルヘッダを読む
	BITMAPFILEHEADER	FileHeader;
	file.read( reinterpret_cast< char *>( &FileHeader), sizeof( BITMAPFILEHEADER));
	// ビットマップのタイプチェック
	if ( !file.good() || (('B' | ( 'M' << 8)) != FileHeader.bfType) )
		return false;
	// イメージデータへのファイル先頭からのオフセット
	int ImageOffset = FileHeader.bfOffBits;

	// パレットの設定
	// ビットマップインフォヘッダーの読み込み
	BITMAPINFOHEADER BitmapInfo;
	file.read( reinterpret_cast< char *>( &BitmapInfo), sizeof( BITMAPINFOHEADER));
	if ( !file.good())
		return false;
	int	PaletteSize = 0;
	int ColorBit = 0;
	int Width = 0;
	int Height = 0;
	// OS/2ビットマップかどうかチェック
	if ( sizeof( BITMAPCOREHEADER) == BitmapInfo.biSize)
	{
		// 読み飛ばし分を差し戻し
		file.seekg( -sizeof( BITMAPINFOHEADER) + sizeof( BITMAPCOREHEADER), ios_base::cur);
		BITMAPCOREHEADER *BitmapCore = reinterpret_cast< BITMAPCOREHEADER *>( &BitmapInfo);
		ColorBit = BitmapCore->bcBitCount;
		Width =  BitmapCore->bcWidth;
		Height = BitmapCore->bcHeight;
		// パレットのサイズを決定
		switch ( BitmapCore->bcBitCount)
		{
		case 1:
		case 24:
			PaletteSize = 0;
			break;
		case 4:
			PaletteSize = 16;
			break;
		case 8:
			PaletteSize = 256;
			break;
		default:
			_ASSERT( 0);
		}
		// パレットサイズがゼロでないなら
		if ( 0 != PaletteSize)
		{
			_ASSERT( 24 != ColorBit);	// 24ビットでパレットはおかしい
			int loop = PaletteSize / 3;
			PaletteSize = loop * 4;
			m_Palette = new unsigned char [ PaletteSize];
			// パレットの読み込み
			for ( int i = 0; i < loop; ++i)
			{
				file.read( reinterpret_cast< char *>( m_Palette) + loop * 4, sizeof( RGBTRIPLE));
				if ( !file.good())
					DeleteBuffer( m_Palette);
			}
			_ASSERT( ImageOffset == file.tellg());
		}
	}
	else	// Windows標準ビットマップの場合
	{
		// 圧縮フォーマットのチェック
		// 圧縮は対応しない
		if ( BI_RGB != BitmapInfo.biCompression && BI_BITFIELDS != BitmapInfo.biCompression)
			return false;
		PaletteSize = ImageOffset - ( sizeof( BITMAPINFOHEADER) + sizeof( BITMAPFILEHEADER));
		ColorBit = BitmapInfo.biBitCount;
		Width = BitmapInfo.biWidth;
		Height = BitmapInfo.biHeight;
		// パレットサイズがゼロでないなら
		if ( 0 != PaletteSize)
		{
			_ASSERT( 24 != ColorBit);
			// パレットの確保
			m_Palette = new unsigned char [ PaletteSize];
			// パレットデータの読み込み
			file.read( reinterpret_cast< char *>( m_Palette), PaletteSize);
			if ( !file.good())
			{
				delete m_Palette;
				return false;
			}
			_ASSERT( ImageOffset == file.tellg());
		}
	}
//---------------------------------------------------
	// イメージの読み込み
	DataSize -= file.tellg();	// 残りバイト数の計算
	m_ImageData = new unsigned char[ DataSize];
	file.read( reinterpret_cast< char *>( m_ImageData), DataSize);
	if ( !file.good())
	{
		DeleteBuffer( m_ImageData);
		DeleteBuffer( m_Palette);
		return false;
	}
	// 各値の設定
	m_ImageSize = DataSize;			// サイズ
	m_PaletteSize =	PaletteSize;
	m_ColorBit = ColorBit;		// カラービット数
	m_Width = Width;		// 幅
	m_Height = Height;		// 高さ
	m_Pitch = CalcPitch( m_Width, m_Height, m_ColorBit);		// ピッチ

	return true;
}

// 指定ファイルパスにビットマップファイルを保存
bool CBitMap::SaveBitmapFile( const string &filename)
{
	if ( 0 == m_ImageSize)
		return false;
	ofstream file( filename.c_str(), ios::out | ios::binary);
	if ( !file.good())
		return false;

	// ファイルヘッダの作成
	BITMAPFILEHEADER	FileHeader;
	ZeroMemory( &FileHeader, sizeof( FileHeader));
	FileHeader.bfType = ( 'B' | ( 'M' << 8));
	FileHeader.bfSize = sizeof( BITMAPFILEHEADER) + sizeof( BITMAPINFOHEADER) + m_PaletteSize + m_ImageSize;
	FileHeader.bfOffBits = sizeof( BITMAPFILEHEADER) + sizeof( BITMAPINFOHEADER) + m_PaletteSize;
	file.write( reinterpret_cast< const char *>( &FileHeader), sizeof( FileHeader));
	if ( !file.good())
		return false;

	// ビットマップ情報の作成
	BITMAPINFOHEADER	BmpInfo;
	ZeroMemory( &BmpInfo, sizeof( BmpInfo));
	BmpInfo.biSize = sizeof( BmpInfo);
	BmpInfo.biWidth = m_Width;
	BmpInfo.biHeight = m_Height;
	BmpInfo.biPlanes = 1;
	BmpInfo.biBitCount = m_ColorBit;
	if ( 16 == m_ColorBit || 32 == m_ColorBit && 0 != m_PaletteSize)
		BmpInfo.biClrImportant = BI_BITFIELDS;
	else
		BmpInfo.biClrImportant = BI_RGB;
	BmpInfo.biSizeImage = m_ImageSize;
	BmpInfo.biClrUsed = m_PaletteSize / 4;
	file.write( reinterpret_cast< const char *>( &BmpInfo), sizeof( BmpInfo));
	if ( !file.good())
		return false;
	// パレットの書き出し
	if ( 0 != m_PaletteSize)
	{
		file.write( reinterpret_cast< const char *>( m_Palette), m_PaletteSize);
		if ( !file.good())
			return false;
	}
	// イメージデータの書き出し
	file.write( reinterpret_cast< const char *>( m_ImageData), m_ImageSize);
	if ( !file.good())
		return false;
	return true;
}
