//////////////////////////////////////////
// ImageConverter.cs
// Copyright (c) by Ysama September, 2024
//////////////////////////////////////////
using System;
using System.Drawing;
using System.Drawing.Imaging;	//ImageCodecInfoを使う為
using System.IO;				//https://learn.microsoft.com/ja-jp/dotnet/api/system.io.path?view=net-8.0
using System.Linq;				//配列のWhere抽出を行う為
using System.Reflection;		//Assemblyを使う為
using System.Windows.Forms;

namespace ImageConverter
{
	public class ImgConverter : Form
	{
		//MimeTypeリスト
		string[] img_MimeType = new string [5] {"image/png", "image/jpeg", "image/bmp", "image/gif", "image/tiff"};
		//拡張子
		string[] img_ext = new string [5] {".png", ".jpg", ".bmp", ".gif", ".tiff"};
		//出力フォールダーパス
		string output_Path = "";
		//出力イメージファイルパス・名
		string output_Name = "";
		//コントロール
		private ListBox listBox;			//変換対象ファイルのファイルパス、名のリスト表示用
		private Button open_button;			//変換対象ファイルの追加用
		private Button del_button;			//変換対象ファイルの削除用
		private Button clr_button;			//リストボックスクリア用
		private Button sel_button;			//変換ファイル出力先の指定用
		private Button cnvt_button;			//ファイル形式の変換用
		private Button exit_button;			//終了用
		private ToolTip tooltip;			//変換ファイル出力先の表示用
		private Label num_label;			//画像説明用
		private Label format_label;			//変換ファイル形式指定用
		private Label w_label;				//変更サイズ幅用
		private Label h_label;				//変更サイズ高用
		private PictureBox pictureBox;		//イメージファイル表示用
		private ComboBox comBox;			//変換ファイル形式指定用
		private CheckBox selected_only;		//選択ファイルのみ（falseの場合はリストボックスの全ファイル）変換
		private CheckBox resizing;			//変換時にサイズを変更する
		private CheckBox asp_ratio;			//縦横比（aspect ratio）維持有無
		private TextBox img_Width;			//変更サイズ幅
		private TextBox img_Height;			//変更サイズ高

		//エントリーポイント
		[STAThread]
		public static void Main() 
		{
			Application.Run(new ImgConverter());
		}

		//コンストラクター
		public ImgConverter()
		{
			this.Icon = Icon.ExtractAssociatedIcon(Assembly.GetEntryAssembly().Location);	//プログラムアイコンをフォームにつける
			this.Size = new Size(496, 518);
			this.MinimumSize = new Size(496, 518);
			this.Text = "Image Converter";
			this.Load += new EventHandler(InitComponent);	//コントロール等の初期化
		}

		//コントロール等の初期化
		private void InitComponent(object sender, EventArgs e)
		{
			//表示停止
			SuspendLayout();
			//リストボックス
			listBox = new ListBox();
			listBox.Location = new Point(10, 10);
			listBox.Size = new Size(280, 148);
			listBox.Anchor = (AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right);
			listBox.HorizontalScrollbar = true;
			listBox.TabIndex = 0;
			//listBox.SelectedIndexChanged += new EventHandler(listBox_SelectedIndexChanged);
			listBox.SelectedIndexChanged += listBox_SelectedIndexChanged;
			listBox.DragEnter += listBox_DragEnter;
			listBox.DragDrop += listBox_DragDrop;
			listBox.AllowDrop = true;
			//ボタン１
			open_button = new Button();
			open_button.Location = new Point(300, 10);
			open_button.Size = new Size(160, 24);
			open_button.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			open_button.Text = "イメージの選択・追加";
			open_button.Click += new EventHandler(open_button_Click);
			clr_button = new Button();
			clr_button.Location = new Point(300,  open_button.Height + 20);
			clr_button.Size = new Size(160, 24);
			clr_button.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			clr_button.Text = "リスト初期化";
			clr_button.Click += new EventHandler(clr_button_Click);
			del_button = new Button();
			del_button.Location = new Point(300, open_button.Height + clr_button.Height + 30);
			del_button.Size = new Size(160, 24);
			del_button.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			del_button.Text = "イメージの削除";
			del_button.Click += new EventHandler(del_button_Click);
			sel_button = new Button();
			sel_button.Location = new Point(300, open_button.Height + del_button.Height + clr_button.Height + 60);
			sel_button.Size = new Size(160, 24);
			sel_button.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			sel_button.Text = "出力先指定";
			sel_button.Click += new EventHandler(sel_button_Click);
			exit_button = new Button();
			exit_button.Location = new Point(300, this.ClientSize.Height - exit_button.Height - 10);
			exit_button.Size = new Size(160, 24);
			exit_button.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right);
			exit_button.Text = "終了";
			exit_button.Click += new EventHandler(exit_button_Click);
			cnvt_button = new Button();
			cnvt_button.Location = new Point(300, this.ClientSize.Height - exit_button.Height - cnvt_button.Height - 20);
			cnvt_button.Size = new Size(160, 24);
			cnvt_button.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right);
			cnvt_button.Text = "イメージ形式を変換";
			cnvt_button.Click += new EventHandler(cnvt_button_Click);
			//ツールチップ
			tooltip = new ToolTip();
			tooltip.ShowAlways = true;
			//ラベル１
			num_label = new Label();
			num_label.AutoSize = true;
			num_label.Location = new Point(10, this.ClientSize.Height - num_label.Height);
			num_label.Anchor = (AnchorStyles.Bottom | AnchorStyles.Left);
			num_label.Text = "イメージ番号：";
			format_label = new Label();
			format_label.Size = new Size(160, 20);
			format_label.Location = new Point(300, open_button.Height + del_button.Height + clr_button.Height + sel_button.Height + 65);
			format_label.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			format_label.TextAlign = ContentAlignment.MiddleCenter;
			format_label.Text = "出力ファイル形式指定";
			//ピクチャーボックス
			pictureBox = new PictureBox();
			pictureBox.BorderStyle = BorderStyle.Fixed3D;
			pictureBox.SizeMode = PictureBoxSizeMode.Zoom;
			pictureBox.Size = new Size(280, 280);
			pictureBox.Location = new Point(10, this.ClientSize.Height - num_label.Height - pictureBox.Height - 8);
			pictureBox.Anchor = (AnchorStyles.Bottom | AnchorStyles.Left);
			//コンボボックス
			comBox = new ComboBox();
			comBox.Location = new Point(300, open_button.Height + del_button.Height + clr_button.Height + sel_button.Height + format_label.Height + 65);
			comBox.Size = new Size(160, 24);
			comBox.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			comBox.DropDownStyle = ComboBoxStyle.DropDownList;
			comBox.Items.AddRange(img_MimeType);
			comBox.SelectedIndex = 0;		//初期値は"image/png"
			//チェックボックス１
			selected_only = new CheckBox();
			selected_only.TextAlign = ContentAlignment.MiddleLeft;
			selected_only.Location = new Point(10, 20);
			selected_only.Size = new Size(160, 24);
			selected_only.Text = "選択ファイルのみ変換";
			resizing = new CheckBox();
			resizing.TextAlign = ContentAlignment.MiddleLeft;
			resizing.Location = new Point(10, selected_only.Height + 25);
			resizing.Size = new Size(160, 24);
			resizing.Text = "ファイルサイズを変更";
			resizing.CheckedChanged += new EventHandler(resizing_CheckedChanged);
			//テキストボックス
			img_Width = new TextBox();
			img_Width.Location = new Point(60, selected_only.Height + resizing.Height + 30);
			img_Width.Size = new Size(100, 24);
			img_Width.Enabled = false;
			img_Height = new TextBox();
			img_Height.Location = new Point(60, selected_only.Height + resizing.Height + img_Width.Height + 35);
			img_Height.Size = new Size(100, 24);
			img_Height.Enabled = false;
			//チェックボックス２
			asp_ratio = new CheckBox();
			asp_ratio.TextAlign = ContentAlignment.MiddleLeft;
			asp_ratio.Location = new Point(60, selected_only.Height + resizing.Height + img_Width.Height + img_Height.Height + 35);
			asp_ratio.Size = new Size(100, 24);
			asp_ratio.Text = "縦横比を維持";
			asp_ratio.Enabled = false;
			//ラベル２
			w_label = new Label();
			w_label.Size = new Size(60, 20);
			w_label.Location = new Point(10, selected_only.Height + resizing.Height + 32);
			w_label.Text = "画像幅";
			h_label = new Label();
			h_label.Size = new Size(60, 20);
			h_label.Location = new Point(10, selected_only.Height + resizing.Height + img_Width.Height + 37);
			h_label.Text = "画像高";
			//オプショングループボックス
			GroupBox groupBox = new GroupBox();
			groupBox.Location = new Point(296, open_button.Height + del_button.Height + clr_button.Height + sel_button.Height + format_label.Height + comBox.Height + 70);
			groupBox.Size = new Size(174, 150);
			groupBox.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			groupBox.Text = "Option";
			groupBox.Controls.Add(selected_only);
			groupBox.Controls.Add(resizing);
			groupBox.Controls.Add(img_Width);
			groupBox.Controls.Add(img_Height);
			groupBox.Controls.Add(w_label);
			groupBox.Controls.Add(h_label);
			groupBox.Controls.Add(asp_ratio);
			//フォームにコントロールを追加
			Controls.Add(listBox);
			Controls.Add(open_button);
			Controls.Add(clr_button);
			Controls.Add(del_button);
			Controls.Add(sel_button);
			Controls.Add(format_label);
			Controls.Add(comBox);
			Controls.Add(groupBox);
			Controls.Add(cnvt_button);
			Controls.Add(exit_button);
			Controls.Add(pictureBox);
			Controls.Add(num_label);
			//表示再開
			ResumeLayout(false);
		}

		//終了処理
		protected override void OnFormClosing(FormClosingEventArgs e)
		{
			base.OnFormClosing(e);
			DialogResult dr = MessageBox.Show("終了しますか？", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
			if(dr == DialogResult.No)
			{
				e.Cancel = true;
			}
		}

		//listBoxの選択項目の変更
		private void listBox_SelectedIndexChanged(object sender, System.EventArgs e)
		{
			if(listBox.SelectedIndex != -1)	//選択されている場合（-1は選択されていない場合）
			{
				//イメージをPictureBox.に表示
				pictureBox.Image = Image.FromFile(listBox.Text);
				num_label.Text = "イメージ番号：" + listBox.SelectedIndex + " (ファイル名：" + Path.GetFileName(listBox.Text) + ")";
				Image img = Image.FromFile(listBox.Text);
				img_Width.Text = img.Width.ToString();
				img_Height.Text = img.Height.ToString();
			}
		}

		//listBoxのDragEnterイベントハンドラ
		private void listBox_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
		{
			//コントロール内にドラッグされたとき実行される
			if(e.Data.GetDataPresent(DataFormats.FileDrop))
				//ドラッグされたデータ形式を調べ、ファイルのときはコピーとする
				e.Effect = DragDropEffects.Copy;
			else
				//ファイル以外は受け付けない
				e.Effect = DragDropEffects.None;
		}

		//listBoxのDragDropイベントハンドラ
		private void listBox_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
		{
			//コントロール内にドロップされたとき実行される
			//ドロップされたすべてのファイル名を取得する
			string[] fileNames = (string[]) e.Data.GetData(DataFormats.FileDrop, false);
			//filenameをimg_ext配列でフィルタリングする
			string[] imgfiles = fileNames.Where(file => img_ext.Contains(Path.GetExtension(file).ToLower())).ToArray();
			//listBoxに追加する
			listBox.Items.AddRange(imgfiles);
		}

		//イメージを開く
		private void open_button_Click (object sender, System.EventArgs e)
		{
			//OpenFileDialogオブジェクトの生成
			OpenFileDialog openDlg = new OpenFileDialog();	//usingを使うと最後のDispose処理が不要になる
			openDlg.Filter = "pngファイル|*.png|jpeg/jpgファイル|*.jpeg;*.jpg|bmpファイル|*.bmp|gifファイル|*.gif|tiffファイル|*.tiff";
			openDlg.Multiselect = true;
			DialogResult result = openDlg.ShowDialog();
			if(result == DialogResult.OK)
			{
				//リストボックスにイメージファイル名を登録
				if(openDlg.FileNames != null)	//複数ファイル選択の場合
				{
					listBox.BeginUpdate();
					for(int i = 0; i < openDlg.FileNames.Length; i++ )
						listBox.Items.Add(openDlg.FileNames[i]);
					listBox.EndUpdate();
				}
				else							//単一ファイル選択の場合
					listBox.Items.Add(openDlg.FileName);
			}
			else if(result == DialogResult.Cancel)
				MessageBox.Show("キャンセルされました。", "キャンセル", MessageBoxButtons.OK, MessageBoxIcon.Information);
			//オブジェクトを破棄する
			openDlg.Dispose();
		}

		//リストボックスで指定したイメージの消去
		private void del_button_Click (object sender, System.EventArgs e)
		{
			if(listBox.SelectedIndex != -1)	//選択されている場合（-1は選択されていない場合）
			{
				listBox.Items.Remove(listBox.SelectedItem);			//リストボックスから消去
				pictureBox.Image = null;							//pictureBoxの画面消去
				num_label.Text = "イメージ番号：";
			}
			else							//-1は選択されていない場合
				MessageBox.Show("選択されていません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
		}

		//すべてのイメージの消去
		private void clr_button_Click (object sender, System.EventArgs e)
		{
			listBox.Items.Clear();					//リストボックスから消去する
			pictureBox.Image = null;				//pictureBoxの画面消去
			num_label.Text = "イメージ番号：";
		}

		//イメージ型式変換
		private void cnvt_button_Click (object sender, System.EventArgs e)
		{
			//エラーチェック－変換対象イメージファイルない場合
			if(listBox.Items.Count == 0)
			{
				MessageBox.Show("変換対象ファイルがありません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}
			//エラーチェック－「ファイルサイズを変更」がチェックされていて、有効な画像幅または画像高が入力されていない場合
			int new_width = 0, new_height = 0;		//入力画像幅と高さ
			if((resizing.CheckState == CheckState.Checked) &&
				!(Int32.TryParse(img_Width.Text, out new_width) && Int32.TryParse(img_Height.Text, out new_height)))
			{
				MessageBox.Show("画像幅または画像高の値が不正です。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}
			//以下オプションに応じて指定画像ファイル形式で出力（同じ形式の場合、ファイル名を変更して出力））
			if(selected_only.CheckState == CheckState.Checked)	//単一ファイルの変換
			{
				//エラーチェック－変換対象ファイルが未選択の場合
				if(listBox.SelectedIndex == -1)
				{
					MessageBox.Show("変換対象ファイルが選択されていません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
					return;
				}
				//出力ファイルパス・名の設定
				//１－ソースファイルパス・名の拡張子を変換対象のものに変更
				output_Name = Path.ChangeExtension(listBox.Text, img_ext[comBox.SelectedIndex]);
				//２－出力先指定があればそのパスと入れ替える
				if(output_Path != "")
					output_Name = output_Path + "\\" + Path.GetFileName(output_Name);
				if(resizing.CheckState == CheckState.Checked)	//ファイルサイズを変更する
				{
					//エラーチェック－ソースファイル拡張子と出力拡張子が同じ場合
					if(Path.GetExtension(listBox.Text) == img_ext[comBox.SelectedIndex])
					{
						output_Name = Path.GetDirectoryName(output_Name) + "\\" + Path.GetFileNameWithoutExtension(output_Name) + "_Rev" + img_ext[comBox.SelectedIndex];
						MessageBox.Show("ソースと同じファイル形式の指定であり、ファイル名を変更しました。\r\n" + output_Name, "注意", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
					}
					Image_Conversion(listBox.Text, output_Name, new_width, new_height, asp_ratio.Checked);
				}
				else											//ファイルサイズを変更しない
				{
					//エラーチェック－ソースファイル拡張子と出力拡張子が同じ場合
					if(Path.GetExtension(listBox.Text) == img_ext[comBox.SelectedIndex])
					{
						MessageBox.Show("ソースと同じファイル形式の指定であり、変換を中止します。\r\n" + output_Name, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
						return;
					}
					Image_Conversion(listBox.Text, output_Name);
				}
			}
			else												//ListBox表示のすべてのファイルの変換
			{
				foreach(object file in listBox.Items)
				{
					//出力ファイルパス・名の設定
					//１－ソースファイルパス・名の拡張子を変換対象のものに変更
					output_Name = Path.ChangeExtension(file.ToString(), img_ext[comBox.SelectedIndex]);
					//２－出力先指定があればそのパスと入れ替える
					if(output_Path != "")
						output_Name = output_Path + "\\" + Path.GetFileName(output_Name);
					if(resizing.CheckState == CheckState.Checked)	//ファイルサイズを変更する
					{
						//エラーチェック－ソースファイル拡張子と出力拡張子が同じ場合
						if(Path.GetExtension(file.ToString()) == img_ext[comBox.SelectedIndex])
						{
							output_Name = Path.GetDirectoryName(output_Name) + "\\" + Path.GetFileNameWithoutExtension(output_Name) + "_Rev" + img_ext[comBox.SelectedIndex];
							MessageBox.Show("ソースと同じファイル形式の指定であり、ファイル名を変更しました。\r\n" + output_Name, "注意", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
						}
						Image_Conversion(file.ToString(), output_Name, new_width, new_height, asp_ratio.Checked);
					}
					else											//ファイルサイズを変更しない
					{
						//エラーチェック－ソースファイル拡張子と出力拡張子が同じ場合
						if(Path.GetExtension(file.ToString()) == img_ext[comBox.SelectedIndex])
						{
							MessageBox.Show("ソースと同じファイル形式の指定であり、変換を中止します。\r\n" + output_Name, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
							continue;
						}
						Image_Conversion(file.ToString(), output_Name);
					}
				}
			}
			output_Path = "";		//出力し終えたので、出力先パスを初期化する
			output_Name = "";		//再初期化
		}

		//出力先（フォールダー）の指定
		private void sel_button_Click(object sender, EventArgs e)
		{
			MessageBox.Show("出力先を指定せず、「キャンセル」すればオリジナル画像と同じフォールダーに保存されます。", "出力先設定上の注意", MessageBoxButtons.OK, MessageBoxIcon.Information);
			FolderBrowserDialog fbDlg = new FolderBrowserDialog();	//usingを使うと最後のDispose処理が不要になる
			// ダイアログの説明文
			fbDlg.Description = "変換したイメージファイルの出力先（フォールダー）を選択してください。";
			// デフォルトのフォルダ（output_Pathにデータがあればそこ、なければ"C:\"）
			fbDlg.SelectedPath = (output_Path == "") ? @"C:\" : output_Path;
			// 「新しいフォルダーの作成する」ボタンを表示する（デフォルトはtrue）
			fbDlg.ShowNewFolderButton = true;
			//フォルダを選択するダイアログを表示する
			if(fbDlg.ShowDialog() == DialogResult.OK)
			{
				output_Path = fbDlg.SelectedPath;
				MessageBox.Show(output_Path, "指定された出力先", MessageBoxButtons.OK, MessageBoxIcon.Information);
				tooltip.SetToolTip(sel_button, output_Path);	//sel_buttonにツールチップをつける
			}
			else
				MessageBox.Show("キャンセルされました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			// オブジェクトを破棄する
			fbDlg.Dispose();
		}

		//フォームを閉じる
		private void exit_button_Click(object sender, EventArgs e)
		{
			Close();
		}

		//resizingチェックボックスの状態が変更された
		private void resizing_CheckedChanged(object sender, EventArgs e)
		{
			if(resizing.CheckState == CheckState.Checked)	//有効化
			{
				img_Width.Enabled = true;	//"画像幅"用ラベル
				img_Height.Enabled = true;	//"画像高"用ラベル
				asp_ratio.Enabled = true;	//"縦横比を維持"用ラベル
				asp_ratio.Checked = true;	//"縦横比を維持"用チェックボックス
			}
			else											//無効化
			{
				img_Width.Enabled = false;	//"画像幅"用ラベル
				img_Height.Enabled = false;	//"画像高"用ラベル
				asp_ratio.Enabled = false;	//"縦横比を維持"用ラベル
				asp_ratio.Checked = false;	//"縦横比を維持"用チェックボックス
			}
		}

		//同一サイズで変換
		private void Image_Conversion(string filename, string output_name)
		{
			//ソースImageオブジェクトの作成
			using(Image image = Image.FromFile(filename))	//自動的にDispose()が呼び出される
			{
				//変換コーデックのImageCodecInfoを作成
				ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
				ImageCodecInfo ici = null;
				for(int i = 0; i < encoders.Length; i++)
					if(encoders[i].MimeType == comBox.SelectedItem.ToString())
						ici = encoders[i];
				//画質パラメーターカテゴリー用EncoderオブジェクトをGUIDに基づいて作成
				//EncoderParameter配列であるEncoderParametersオブジェクトの作成
				EncoderParameters encoderParameters = new EncoderParameters(1);
				//出力用Imageをレベル100の画質で保存
				encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
				image.Save(output_name, ici, encoderParameters);
			}
		}

		//サイズを変更して変換（オーバーロード関数－アスペクト比を維持）
		private void Image_Conversion(string filename, string output_name, int width, int height, bool aspectRatio = true)
		{
			//ソースImageオブジェクトの作成
			float scale;
			int new_width = width, new_height = height;
			using(Image image = Image.FromFile(filename))			//自動的にDispose()が呼び出される
			{
				//変更倍率を取得する
				if(aspectRatio)
				{
					scale = Math.Min((float)width / (float)image.Width,
									(float)height / (float)image.Height);
					//変更サイズを取得
					new_width = (int)(image.Width * scale);
					new_height = (int)(image.Height * scale);
				}
				//変換用Bitmapオブジェクトの作成
				using(Bitmap bmp = new Bitmap(width, height))		//自動的にDispose()が呼び出される
				using(Graphics graphics = Graphics.FromImage(bmp))	//自動的にDispose()が呼び出される
				{
					//背景色（黒）
					using(SolidBrush solidBrush = new SolidBrush(Color.Black))
					{	//自動的にDispose()が呼び出される
						graphics.FillRectangle(solidBrush, new RectangleF(0, 0, width, height));
					}
					//左上起点で変更画像を描画
					graphics.DrawImage(image, 0, 0, new_width, new_height);
					//変換コーデックのImageCodecInfoを作成
					ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
					ImageCodecInfo ici = null;
					for(int i = 0; i < encoders.Length; i++)
						if(encoders[i].MimeType == comBox.SelectedItem.ToString())
							ici = encoders[i];
					//画質パラメーターカテゴリー用EncoderオブジェクトをGUIDに基づいて作成
					//EncoderParameter配列であるEncoderParametersオブジェクトの作成
					EncoderParameters encoderParameters = new EncoderParameters(1);
					//出力用Imageをレベル100の画質で保存
					encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
					bmp.Save(output_name, ici, encoderParameters);
				}
			}
		}
	}
}
