//////////////////////////////////////////
// ResizerProc.h
// Copyright (c) 03/27/2022 by BCCSkelton
//////////////////////////////////////////

/////////////////////////////////
//主ウィンドウCRESIZERの関数の定義
//ウィンドウメッセージ関数
/////////////////////////////////
bool CRESIZER::OnInit(WPARAM wParam, LPARAM lParam) {

	//ワイド文字使用のためロケールの初期化（日本語）
	setlocale(LC_CTYPE, "JPN");
	//GDI+の開始
	GdiplusStartup(&m_gdiToken, &m_gdiSI, NULL);
	//リストビューウィンドウハンドル
	m_ListView.m_hWnd = GetDlgItem(m_hWnd, IDC_LISTVIEW);
	//リストビュー親ウィンドウハンドル
	m_ListView.m_hParent = m_hWnd;
	//リストビューインスタンス
	m_ListView.m_hInstance = m_hInstance;
	//リストビューコントロールID
	m_ListView.m_hMenu = (HMENU)IDC_LISTVIEW;
	//リストビュー列名と幅の設定
	m_ListView.InsertColumn(0, 30, "No", LVCFMT_CENTER);
	m_ListView.InsertColumn(1, 184, "ファイル名");
	m_ListView.InsertColumn(2, 100, "幅");
	m_ListView.InsertColumn(3, 100, "高さ");
	m_ListView.InsertColumn(4, 100, "修正幅");
	m_ListView.InsertColumn(5, 100, "修正高さ");
	//リストビュー拡張スタイルの設定
	INITCOMMONCONTROLSEX ic;
	ic.dwSize = sizeof(ic);
	ic.dwICC = ICC_LISTVIEW_CLASSES;
	InitCommonControlsEx(&ic);
	DWORD lvStyle = SendItemMsg(IDC_LISTVIEW, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
	lvStyle |= LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES;
	SendItemMsg(IDC_LISTVIEW, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES, lvStyle);
	return TRUE;
}

/////////////////////////////////
//主ウィンドウCRESIZERの関数の定義
//メニュー項目、コントロール関数
/////////////////////////////////
bool CRESIZER::OnSelsrc() {

	char* cp = g_Cmndlg.GetPath(m_hWnd, "ソースファイルパスの選択");
	if(!cp) {
		MessageBox(m_hWnd, "キャンセルされました", "エラー", MB_OK | MB_ICONERROR);
		return FALSE;
	}
	//ソースファイルパス（最後の'\'付）
	m_SrcPath = cp;
	m_SrcPath = m_SrcPath + "\\";
	SendItemMsg(IDC_SOURCE, WM_SETTEXT, 0, (LPARAM)m_SrcPath.ToChar());
	//リストビューにファイルを列挙
	SetFileNames(cp);
	return TRUE;
}

bool CRESIZER::OnOverwrite() {

	switch(SendItemMsg(IDC_OVERWRITE, BM_GETCHECK, 0, 0)) {
	case BST_CHECKED:
		//ソースの内容をデスティネーションにコピー
		m_DstPath = m_SrcPath;
		//ディスティネーションエディットボックスに表示
		SendItemMsg(IDC_DIST, WM_SETTEXT, 0, (LPARAM)m_DstPath.ToChar());
		//デスティネーション選択ボタンを無効化
		EnableWindow(GetDlgItem(m_hWnd, IDC_SELDIST), FALSE);
		break;
	case BST_UNCHECKED:
		//デスティネーションエディットボックスを消去
		SendItemMsg(IDC_DIST, WM_SETTEXT, 0, (LPARAM)"");
		//ディスティネーションファイルパスクリア
		m_DstPath = "";
		//デスティネーション選択ボタンを有効化
		EnableWindow(GetDlgItem(m_hWnd, IDC_SELDIST), TRUE);
		break;
	default:
		return FALSE;
	}
	return TRUE;
}

bool CRESIZER::OnSeldist() {

	char* cp = g_Cmndlg.GetPath(m_hWnd, "コピー先フォールダーの選択");
	if(!cp) {
		MessageBox(m_hWnd, "キャンセルされました", "エラー", MB_OK | MB_ICONERROR);
		return FALSE;
	}
	//ディスティネーションファイルパス（最後の'\'付）
	m_DstPath = cp;
	m_DstPath = m_DstPath + "\\";
	SendItemMsg(IDC_DIST, WM_SETTEXT, 0, (LPARAM)m_DstPath.ToChar());
	return TRUE;
}

bool CRESIZER::OnIdgo() {

	//縮小コピー処理数
	m_Noc = 0;
	//作業用文字列バッファ
	char buff[MAX_PATH];
	//ソースファイルフォールダーの取得チェック
	if(!*m_SrcPath.ToChar()) {
		MessageBox(m_hWnd, "ソースファイルパスが未入力です", "エラー",
					MB_OK | MB_ICONERROR);
		return FALSE;
	}
	//一辺の最大値を取得チェック
	SendItemMsg(IDC_MAX, WM_GETTEXT, MAX_PATH, (LPARAM)buff);
	if(!*buff || !IsDigitStr(buff)) {
		MessageBox(m_hWnd, "一辺の最大値が未入力または不正です", "エラー",
					MB_OK | MB_ICONERROR);
		return FALSE;
	}
	m_Max = atoi(buff);
	//ディスティネーションファイルフォールダーの取得チェック
	if(!*m_DstPath.ToChar()) {
		MessageBox(m_hWnd, "ディスティネーションファイルパスが未入力です", "エラー",
					MB_OK | MB_ICONERROR);
		return FALSE;
	}
	//上書き(TRUE)か否かの確認
	bool overwrite = (SendItemMsg(IDC_OVERWRITE, BM_GETCHECK, 0, 0) == BST_CHECKED);
	//リストビューのファイル名でイメージをコピーする
	for(int i = 0; i < m_NoOfFiles; i++)
		if(CopyImages(i, overwrite))
			m_Noc++;
	//終了通知
	wsprintf(buff, "条件を満たした%d個のファイルを縮小処理しました。", m_Noc);
	MessageBox(m_hWnd, buff, "終了", MB_OK | MB_ICONINFORMATION);
	return TRUE;
}

bool CRESIZER::OnIdok() {

	EndModal(m_Noc);
	return TRUE;
}

bool CRESIZER::OnIdcancel() {

	EndModal(FALSE);
	return TRUE;
}

bool CRESIZER::OnClose(WPARAM wParan, LPARAM lParam) {

	GdiplusShutdown(m_gdiToken);
	return TRUE;
}

//////////////////////////////////
//ユーザー定義関数 - SetFileNames
//・pathで指定するフォールダー内の
//　ファイル名をリストビューに登録
//・m_NoOfFilesにファイル数を代入
//・エラーまたはファイルが無い場合
//　FALSEを返す
//////////////////////////////////
bool CRESIZER::SetFileNames(char* path) {

	//リストビューをクリアーし、ファイル数を0にする
	m_ListView.DeleteAllItems();
	m_NoOfFiles = 0;
	//ファイル検索
	char buff[MAX_PATH];			//作業用文字列バッファ
	CSTR FileName(path);
	FileName = FileName + "\\*.*";	//ファイル名検索用パス + ワイルドカード
	WIN32_FIND_DATA fd;
	HANDLE hFind = FindFirstFile(FileName.ToChar(), &fd);
	if(hFind == INVALID_HANDLE_VALUE) {
		MessageBox(m_hWnd, "ファイル情報取得失敗", "エラー", MB_OK | MB_ICONERROR);
		return FALSE;
	}
	else {
		do {
			//ディレクトリーの場合何もしない
			if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
			//ファイルの場合
			else {
				//GDI+の対象となる画像ファイルの場合
				if(g_ExtChk.CheckExt(fd.cFileName, IMGFILTER)) {
					//リストボックスに表示(番号)
					wsprintf(buff, "%d", m_NoOfFiles);
					m_ListView.InsertItem(m_NoOfFiles, buff, -1, m_NoOfFiles);
					//ファイル名
					m_ListView.SetItem(m_NoOfFiles, 1, fd.cFileName);
					m_NoOfFiles++;
				}
			}
		} while(FindNextFile(hFind, &fd));
	}
	FindClose(hFind);
	return TRUE;
}

//////////////////////////////////
//ユーザー定義関数 - IsDigitStr
//・strで指定する文字列が'0'　-'9'
//　迄の数字文字か否かチェックする
//・そうであればTRUE、数字以外があ
//　ればFALSEを返す
//////////////////////////////////
bool CRESIZER::IsDigitStr(char* str) {

	for(char* cp = str; *cp; cp++) {
		if(*cp > '9' || *cp < '0')
			return FALSE;
	}
	return TRUE;
}

/////////////////////////////////////////////////
//ユーザー定義関数 - CopyImages
//・m_ListViewの引数n行、第2列のファイル名を取得
//・m_SrcPath + ファイル名の画像インスタンスを生
//　成して、幅、高さを取得
//・m_Max と比較して、一辺がそれより大きければ長
//　い辺をm_Max とし、短い辺を画像の縦横比に応じ
//　て修正
//・修正幅、高さのビットマップフレームを生成し、
//　そこに縮小画像を書き込む
//・書き込まれたビットマップを、オリジナル画像の
//　エンコーダーを用いてm_DstPath + ファイル名で
//　書き込む
//・「上書き」モードの場合は、ファイル名を"*.bak"
//　とし、オリジナルを削除、新ファイルの名前をオ
//　リジナルのものに変える
/////////////////////////////////////////////////
bool CRESIZER::CopyImages(int n, bool overwrite) {

	//変数宣言
	int Width;						//イメージの幅
	int Height;						//イメージの高さ
	int AdjW;						//修正後幅
	int AdjH;						//修正後高さ
	CSTR SrcFile, DstFile;			//ソースファイル名、書き込みファイル名
	WCHAR WFName[MAX_PATH];			//フルパスファイル名用ワイド文字列(WCS）
	char name[MAX_PATH];			//リストボックスのファイル名
	char buff[MAX_PATH];			//作業用文字列バッファ
	//リストビューから読み込みファイル名を取得
	m_ListView.GetItemText(n, 1, name, MAX_PATH);
	//読み込みファイル名の作成
	SrcFile = m_SrcPath;
	SrcFile = SrcFile + name;				//m_SrcPathは既に'\'付
	//書き込みファイル名の作成
	if(overwrite)							//nameの拡張子を"bak"に変える
		lstrcpy(strstr(name, "."), ".bak");	//g_ChkExtを経由しており'.'がある
	DstFile = m_DstPath;
	DstFile = DstFile + name;				//m_DstPathは既に'\'付
	//Imageインスタンスの生成（ファイル名はワイド文字化）
	mbstowcs(WFName, SrcFile.ToChar(), MAX_PATH);
	m_pImage = new (Image)(WFName);
	//イメージの幅、高さを取得
	Width  = m_pImage->GetWidth();
	Height = m_pImage->GetHeight();
	//リストボックスの表示（１）
	wsprintf(buff, "%d", Width);	//イメージ幅
	m_ListView.SetItem(n, 2, buff);
	wsprintf(buff, "%d", Height);	//イメージ高さ
	m_ListView.SetItem(n, 3, buff);
	//一片の最大値から原寸縦横比のまま修正幅、高さを算出
	if(Width > m_Max || Height > m_Max) {
		if(Width > Height) {
			AdjH = Height * ((double)m_Max / (double)Width);
			AdjW = m_Max;
		}
		else {
			AdjW = Width * ((double)m_Max / (double)Height);
			AdjH = m_Max;
		}
	}
	//縦横共にm_Maxを超えない場合はFALSEで戻る
	else {
		if(!overwrite)	//上書きでなければ
			//条件に適合しないファイルはそのままコピーする
			CopyFile(SrcFile.ToChar(), DstFile.ToChar(), FALSE);
		delete m_pImage;
		return FALSE;
	}
	//リストボックスの表示（２）
	wsprintf(buff, "%d", AdjW);		//修正幅
	m_ListView.SetItem(n, 4, buff);
	wsprintf(buff, "%d", AdjH);		//修正高さ
	m_ListView.SetItem(n, 5, buff);
	//縮小画像用のBitmapオブジェクトをオリジナルの縦横比で生成
	Bitmap* pBitmap = new Bitmap(AdjW, AdjH);
	//Bitmapオブジェクトから出力用のGraphicsフレームオブジェクトを作成
	Graphics* pFrame = Graphics::FromImage(pBitmap);
	//Rectオブジェクトのフレームの矩形を生成
	Gdiplus::Rect rtFrame(0, 0, AdjW, AdjH);
	//画像の全体をフレームのrtFrameで表した矩形領域へ描画
	pFrame->DrawImage(m_pImage, rtFrame, 0, 0, Width, Height, UnitPixel);	//1ポイント == 1/72インチ
	//書き込み用エンコーダのCLSIDを取得
	int i = g_ExtChk.CheckExt(SrcFile.ToChar(), IMGFILTER);					//画像形式をチェック
	if(!i) {
		MessageBox(m_hWnd, "対象ファイルではありません", "エラー", MB_OK | MB_ICONERROR);
		//ビットマップのメモリーを解放
		delete pBitmap;
		//m_Imageインスタンスを解放
		delete m_pImage;
		return FALSE;
	}
	else if(i > 2)
		i--;
	CLSID encoderClsid;
	//g_EncoderList配列のエンコーダー型を使ってCLSIDを取り出す
	if(GetEncoderClsid(g_EncoderList[i - 1], &encoderClsid) == -1) {
		MessageBox(m_hWnd, DstFile.ToChar(), "エンコーダーCLSIDの取得失敗", MB_OK | MB_ICONERROR);
		//ビットマップのメモリーを解放
		delete pBitmap;
		//m_Imageインスタンスを解放
		delete m_pImage;
		return FALSE;
	}
	//EncoderParameters配列の第1オブジェクトを使って品質を指定
	LONG lQuality = 100;	//品質レベルは100の最高品質
	//EncoderParameters配列オブジェクト
	EncoderParameters EncoderParams;
	//先頭のEncoderParameterオブジェクト
	EncoderParams.Parameter[0].Guid = EncoderQuality;					//品質であることを指定
	EncoderParams.Parameter[0].NumberOfValues = 1;						//Valueの変数数は1つ
	EncoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;	//Valueの変数の長さ
	EncoderParams.Parameter[0].Value = (VOID*) &lQuality;				//品質指定
	EncoderParams.Count = 1;											//EncoderParameters配列は一つだけ
	//書き込みファイル名のワイド文字版を作る
	mbstowcs(WFName, DstFile.ToChar(), MAX_PATH);
	//フレームとして使ったBitmapを指定エンコーダー、品質でファイルに保存
	pBitmap->Save(WFName, &encoderClsid, &EncoderParams);
	//ビットマップのメモリーを解放
	delete pBitmap;
	//m_Imageインスタンスを解放
	delete m_pImage;
	//上書き処理
	if(overwrite) {
		DeleteFile(SrcFile.ToChar());					//オリジナルファイルを削除
		MoveFile(DstFile.ToChar(), SrcFile.ToChar());	//縮小ファイルをオリジナル名に変更
	}
	return TRUE;
}

////////////////////////////////////////////////////
//ユーザー定義関数
//Image CODEC CLSIDの取得（出展：Microsoft Doc）
//formatに合致するCODECのCLSIDアドレスをpClsidに渡す
//戻り値：成功−Image CODEC配列(ImageCodecInfo)引数
//　　　　失敗− -1
////////////////////////////////////////////////////
int CRESIZER::GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {

	//イメージCODECの数、サイズ取得
	UINT  num = 0;		//image encoderの数
	UINT  size = 0;		//image encoder配列のサイズ（バイト数）
	GetImageEncodersSize(&num, &size);
	if(size == 0) {
		return -1; //イメージCODECサイズ取得失敗
	}
	//イメージCODEC情報用クラス(ImageCodecInfo)
	ImageCodecInfo* pImageCodecInfo = NULL;
	pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
	if(pImageCodecInfo == NULL) {
		return -1; //メモリ確保失敗
	}
	//イメージCODEC情報クラス取得
	GetImageEncoders(num, size, pImageCodecInfo);

	for(UINT j = 0; j < num; ++j) {
		//formatがCODECのMIME(Multipurpose Internet Mail Extensions)型なら
		if(wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
			//J番目のCODECのグローバル一意識別子（CLSID）を渡す
			*pClsid = pImageCodecInfo[j].Clsid;
			free(pImageCodecInfo);
			return j;	//成功−
		}  
	}
	free(pImageCodecInfo);
	return -1;			// 失敗 - 要求した種類のエンコーダが存在しなかった
}
