using System;
using System.Media;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

namespace Mix.Tool
{
    /// <summary>
    /// メッセージ ダイアログに表示するボタンを定義する定数
    /// </summary>
    public enum MessageDialogButtons
    {
        /// <summary>
        /// メッセージ ダイアログに [OK] ボタンを含めます
        /// </summary>
        OK,

        /// <summary>
        /// メッセージ ダイアログに [OK] ボタンと [キャンセル] ボタンを含めます
        /// </summary>
        OKCancel,

        /// <summary>
        /// メッセージ ダイアログに [中止]、[再試行]、および [無視] の各ボタンを含めます
        /// </summary>
        AbortRetryIgnore,

        /// <summary>
        /// メッセージ ダイアログに [はい]、[いいえ]、および [キャンセル] の各ボタンを含めます
        /// </summary>
        YesNoCancel,

        /// <summary>
        /// メッセージ ダイアログに [はい] ボタンと [いいえ] ボタンを含めます
        /// </summary>
        YesNo,

        /// <summary>
        /// メッセージ ダイアログに [再試行] ボタンと [キャンセル] ボタンを含めます
        /// </summary>
        RetryCancel,
    }

    /// <summary>
    /// メッセージ ダイアログに表示するアイコンを定義する定数
    /// </summary>
    public enum MessageDialogIcon
    {
        /// <summary>
        /// メッセージ ダイアログにアイコンを表示しません
        /// </summary>
        None,

        /// <summary>
        /// メッセージ ダイアログに、背景が赤の円で囲まれた白い X から成る記号を表示します
        /// </summary>
        Hand,

        /// <summary>
        /// メッセージ ダイアログに、円で囲まれた疑問符から成る記号を表示します
        /// </summary>
        Question,

        /// <summary>
        /// メッセージ ダイアログに、背景が黄色の三角形で囲まれた感嘆符から成る記号を表示します
        /// </summary>
        Exclamation,

        /// <summary>
        /// メッセージ ダイアログに、円で囲まれた小文字の i から成る記号を表示します
        /// </summary>
        Asterisk,

        /// <summary>
        /// メッセージ ダイアログに、背景が赤の円で囲まれた白い X から成る記号を表示します
        /// </summary>
        Stop,

        /// <summary>
        /// メッセージ ダイアログに、背景が赤の円で囲まれた白い X から成る記号を表示します
        /// </summary>
        Error,

        /// <summary>
        /// メッセージ ボックスに、背景が黄色の三角形で囲まれた感嘆符から成る記号を表示します
        /// </summary>
        Warning,

        /// <summary>
        /// メッセージ ボックスに、円で囲まれた小文字の i から成る記号を表示します
        /// </summary>
        Information,
    }

    /// <summary>
    /// メッセージ ダイアログに表示する既定のボタンを定義する定数
    /// </summary>
    public enum MessageDialogDefaultButton
    {
        /// <summary>
        /// メッセージ ダイアログの 1 番目のボタンが既定
        /// </summary>
        Button1,

        /// <summary>
        /// メッセージ ダイアログの 2 番目のボタンが既定
        /// </summary>
        Button2,

        /// <summary>
        /// メッセージ ダイアログの 3 番目のボタンが既定
        /// </summary>
        Button3,
    }

    /// <summary>
    /// メッセージダイアログクラス
    /// </summary>
    public class MessageDialog
    {
        private class Main : Form
        {
            private const int ButtonMargin = 8;
            private const int MarginXY = 12;
            private const float MinButtonWidth = 80;
            private const float MinButtonHeight = 32;

            private string message = "";
            private MessageDialogButtons buttons = MessageDialogButtons.OK;
            private MessageDialogIcon iconType = MessageDialogIcon.None;
            private MessageDialogDefaultButton defaultButton = MessageDialogDefaultButton.Button1;

            private Icon icon = null;
            private Point iconPos = new Point();
            private PointF messagePosF = new PointF();
            private Rectangle messageRect = new Rectangle();

            public DialogResult Show(string _text, string _caption, MessageDialogButtons _buttons, MessageDialogIcon _iconType, MessageDialogDefaultButton _defaultButton)
            {
                Graphics grap = CreateGraphics();
                SizeF messageSizeF = grap.MeasureString(_text, Font);
                Size messageSize = new Size((int)messageSizeF.Width, (int)messageSizeF.Height);
                SizeF buttonSizeF = new SizeF(MinButtonWidth, MinButtonHeight);
                Size buttonSize = new Size();
                Size buttonsSize = new Size();
                Size clientSize = new Size();
                List<Button> buttonList = new List<Button>();
                SystemSound sound = null;

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // 設定
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                Font = SystemInformation.MenuFont;
                Text = _caption;

                message = _text;
                buttons = _buttons;
                iconType = _iconType;
                defaultButton = _defaultButton;

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // 初期化
                ////////////////////////////////////////////////////////////////////////////////////////////////////
 
                grap = CreateGraphics();
                messageSizeF = grap.MeasureString(message, Font);
                messageSize = new Size((int)messageSizeF.Width, (int)messageSizeF.Height);
                buttonSizeF = new SizeF(MinButtonWidth, MinButtonHeight);
                buttonSize = new Size();
                buttonsSize = new Size();
                clientSize = new Size();
                buttonList = new List<Button>();
                sound = null;

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // アイコンの事前処理
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                switch (iconType)
                {
                    case MessageDialogIcon.Asterisk:
                        icon = SystemIcons.Asterisk;
                        sound = SystemSounds.Asterisk;
                        break;
                    case MessageDialogIcon.Error:
                        icon = SystemIcons.Error;
                        sound = SystemSounds.Hand;
                        break;
                    case MessageDialogIcon.Exclamation:
                        icon = SystemIcons.Exclamation;
                        sound = SystemSounds.Exclamation;
                        break;
                    case MessageDialogIcon.Hand:
                        icon = SystemIcons.Hand;
                        sound = SystemSounds.Hand;
                        break;
                    case MessageDialogIcon.Information:
                        icon = SystemIcons.Information;
                        sound = SystemSounds.Asterisk;
                        break;
                    case MessageDialogIcon.Question:
                        icon = SystemIcons.Question;
                        sound = SystemSounds.Question;
                        break;
                    case MessageDialogIcon.Stop:
                        icon = SystemIcons.Error;
                        sound = SystemSounds.Hand;
                        break;
                    case MessageDialogIcon.Warning:
                        icon = SystemIcons.Warning;
                        sound = SystemSounds.Question;
                        break;
                    default:
                        icon = null;
                        sound = null;
                        break;
                }

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // 表示するボタンの事前処理
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                Button tempButton;

                string[] buttonStrList =
                {
                    Properties.Resources.Str_Abort,
                    Properties.Resources.Str_Cancel,
                    Properties.Resources.Str_Ignore,
                    Properties.Resources.Str_No,
                    Properties.Resources.Str_OK,
                    Properties.Resources.Str_Retry,
                    Properties.Resources.Str_Yes,
                };

                foreach (string str in buttonStrList)
                {
                    SizeF strSizeF = grap.MeasureString(str, Font);

                    if (buttonSizeF.Width < strSizeF.Width)
                    {
                        buttonSizeF.Width = strSizeF.Width;
                    }

                    if (buttonSizeF.Height < strSizeF.Height)
                    {
                        buttonSizeF.Height = strSizeF.Height;
                    }
                }

                buttonSize.Width = (int)buttonSizeF.Width;
                buttonSize.Height = (int)buttonSizeF.Height;

                switch (buttons)
                {
                    case MessageDialogButtons.AbortRetryIgnore:
                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_Abort;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.Abort;
                        buttonList.Add(tempButton);

                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_Retry;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.Retry;
                        buttonList.Add(tempButton);

                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_Ignore;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.Ignore;
                        Controls.Add(tempButton);
                        buttonList.Add(tempButton);
                        break;

                    case MessageDialogButtons.OK:
                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_OK;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.OK;
                        buttonList.Add(tempButton);
                        break;

                    case MessageDialogButtons.OKCancel:
                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_OK;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.OK;
                        buttonList.Add(tempButton);

                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_Cancel;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.Cancel;
                        buttonList.Add(tempButton);
                        break;

                    case MessageDialogButtons.RetryCancel:
                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_Retry;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.Retry;
                        buttonList.Add(tempButton);

                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_Cancel;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.Cancel;
                        buttonList.Add(tempButton);
                        break;

                    case MessageDialogButtons.YesNo:
                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_Yes;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.Yes;
                        buttonList.Add(tempButton);

                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_No;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.No;
                        buttonList.Add(tempButton);
                        break;

                    case MessageDialogButtons.YesNoCancel:
                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_Yes;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.Yes;
                        buttonList.Add(tempButton);

                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_No;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.No;
                        buttonList.Add(tempButton);

                        tempButton = new Button();
                        tempButton.Text = Properties.Resources.Str_Cancel;
                        tempButton.Size = buttonSize;
                        tempButton.DialogResult = DialogResult.Cancel;
                        Controls.Add(tempButton);
                        buttonList.Add(tempButton);
                        break;
                }

                //すべてのボタンを囲むサイズ
                buttonsSize.Width = (((buttonList.Count - 1) * ButtonMargin) + (buttonList.Count * buttonSize.Width));
                buttonsSize.Height = buttonSize.Height;

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // クライアントのサイズ
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                clientSize.Width = (MarginXY * 4);
                clientSize.Height = (MarginXY * 4);

                if (icon != null)
                {
                    //アイコンがある場合

                    clientSize.Width += icon.Width;
                    clientSize.Width += MarginXY;

                    if (buttonsSize.Width < messageSize.Width)
                    {
                        clientSize.Width += messageSize.Width;
                    }
                    else
                    {
                        clientSize.Width += buttonsSize.Width;
                    }

                    if (icon.Height < messageSize.Height)
                    {
                        clientSize.Height += messageSize.Height;
                    }
                    else
                    {
                        clientSize.Height += icon.Height;
                    }

                    clientSize.Height += (MarginXY + MarginXY);
                    clientSize.Height += buttonsSize.Height;
                }
                else
                {
                    //アイコンが無い場合

                    if (buttonsSize.Width < messageSize.Width)
                    {
                        clientSize.Width += messageSize.Width;
                    }
                    else
                    {
                        clientSize.Width += buttonsSize.Width;
                    }

                    clientSize.Height += messageSize.Height;
                    clientSize.Height += (MarginXY + MarginXY);
                    clientSize.Height += buttonsSize.Height;
                }

                messageRect.X = 0;
                messageRect.Y = 0;
                messageRect.Width = clientSize.Width;
                messageRect.Height = (clientSize.Height - (MarginXY * 2) - buttonsSize.Height);

                if (icon != null)
                {
                    if (icon.Height < messageSize.Height)
                    {
                        iconPos.X = (MarginXY * 2);
                        iconPos.Y = ((MarginXY * 2) + (messageSize.Height - icon.Height) / 2);

                        messagePosF.X = (iconPos.X + icon.Width + MarginXY);
                        messagePosF.Y = (MarginXY * 2);
                    }
                    else
                    {
                        iconPos.X = (MarginXY * 2);
                        iconPos.Y = (MarginXY * 2);

                        messagePosF.X = (iconPos.X + icon.Width + MarginXY);
                        messagePosF.Y = ((MarginXY * 2) + (icon.Height - messageSize.Height) / 2);
                    }
                }
                else
                {
                    messagePosF.X = (clientSize.Width - messageSize.Width) / 2;
                    messagePosF.Y = MarginXY;
                }

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // クライアントのサイズを設定
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                ClientSize = clientSize;

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // ボタンを追加する
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                int buttonPosX = (clientSize.Width - MarginXY - buttonsSize.Width);
                int buttonPosY = (clientSize.Height - MarginXY - buttonsSize.Height);

                foreach (Button btn in buttonList)
                {
                    btn.Location = new Point(buttonPosX, buttonPosY);
                    buttonPosX += (ButtonMargin + buttonSize.Width);

                    Controls.Add(btn);
                }

                switch (defaultButton)
                {
                    case MessageDialogDefaultButton.Button1:
                        ActiveControl = buttonList[0];
                        break;
                    case MessageDialogDefaultButton.Button2:
                        if (buttonList.Count >= 2)
                        {
                            ActiveControl = buttonList[1];
                        }
                        else
                        {
                            ActiveControl = buttonList[0];
                        }
                        break;
                    case MessageDialogDefaultButton.Button3:
                        if (buttonList.Count >= 3)
                        {
                            ActiveControl = buttonList[2];
                        }
                        else
                        {
                            ActiveControl = buttonList[0];
                        }
                        break;
                }

                buttonList.Clear();

                grap.Dispose();

                MinimizeBox = false;
                MaximizeBox = false;
                StartPosition = FormStartPosition.CenterParent;

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // 音を鳴らす
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                sound.Play();

                return ShowDialog();
            }
 
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);

                e.Graphics.FillRectangle(SystemBrushes.Window, messageRect);

                if (icon != null)
                {
                    e.Graphics.DrawIcon(icon, iconPos.X, iconPos.Y);
                }

                e.Graphics.DrawString(message, Font, SystemBrushes.WindowText, messagePosF);
            }
            protected override CreateParams CreateParams
            {
                get
                {
                    const int CS_NOCLOSE = 0x200;
                    CreateParams cp = base.CreateParams;
                    cp.ClassStyle = (cp.ClassStyle | CS_NOCLOSE);
                    return cp;
                }
            }
        }

        /// <summary>
        /// メッセージダイアログを表示します
        /// </summary>
        /// <param name="text">表示するテキスト</param>
        /// <param name="caption">タイトルバーに表示するテキスト</param>
        /// <param name="buttons">表示するボタン</param>
        /// <returns>DialogResult 値のいずれか</returns>
        public static DialogResult Show(string text, string caption, MessageDialogButtons buttons)
        {
            return Show(text, caption, buttons, MessageDialogIcon.None, MessageDialogDefaultButton.Button1);
        }

        /// <summary>
        /// メッセージダイアログを表示します
        /// </summary>
        /// <param name="text">表示するテキスト</param>
        /// <param name="caption">タイトルバーに表示するテキスト</param>
        /// <param name="buttons">表示するボタン</param>
        /// <param name="defaultButton">既定のボタン</param>
        /// <returns>DialogResult 値のいずれか</returns>
        public static DialogResult Show(string text, string caption, MessageDialogButtons buttons, MessageDialogDefaultButton defaultButton)
        {
            return Show(text, caption, buttons, MessageDialogIcon.None, defaultButton);
        }

        /// <summary>
        /// メッセージダイアログを表示します
        /// </summary>
        /// <param name="text">表示するテキスト</param>
        /// <param name="caption">タイトルバーに表示するテキスト</param>
        /// <param name="buttons">表示するボタン</param>
        /// <param name="icon">表示するアイコン</param>
        /// <returns>DialogResult 値のいずれか</returns>
        public static DialogResult Show(string text, string caption, MessageDialogButtons buttons, MessageDialogIcon icon)
        {
            return Show(text, caption, buttons, icon, MessageDialogDefaultButton.Button1);
        }

        /// <summary>
        /// メッセージダイアログを表示します
        /// </summary>
        /// <param name="text">表示するテキスト</param>
        /// <param name="caption">タイトルバーに表示するテキスト</param>
        /// <param name="buttons">表示するボタン</param>
        /// <param name="icon">表示するアイコン</param>
        /// <param name="defaultButton">既定のボタン</param>
        /// <returns>DialogResult 値のいずれか</returns>
        public static DialogResult Show(string text, string caption, MessageDialogButtons buttons, MessageDialogIcon icon, MessageDialogDefaultButton defaultButton)
        {
            DialogResult result;

            using (Main dlg = new Main())
            {
                dlg.FormBorderStyle = FormBorderStyle.FixedDialog;
                result = dlg.Show(text, caption, buttons, icon, defaultButton);
            }

            return result;
        }

        /// <summary>
        /// OKボタンのついたメッセージダイアログを表示します
        /// </summary>
        /// <param name="text">表示するテキスト</param>
        /// <param name="caption">タイトルバーに表示するテキスト</param>
        /// <param name="icon">表示するアイコン</param>
        public static void ShowOK(string text, string caption, MessageDialogIcon icon)
        {
            Show(text, caption, MessageDialogButtons.OK, icon);
        }

        /// <summary>
        /// Yes No ボタンのついたメッセージダイアログを表示します
        /// </summary>
        /// <param name="text">表示するテキスト</param>
        /// <param name="caption">タイトルバーに表示するテキスト</param>
        /// <param name="icon">表示するアイコン</param>
        /// <returns>DialogResult.Yes DialogResult.No のいずれか</returns>
        public static DialogResult ShowYN(string text, string caption, MessageDialogIcon icon)
        {
            return Show(text, caption, MessageDialogButtons.YesNo, icon, MessageDialogDefaultButton.Button2);
        }

        /// <summary>
        /// Yes No Cancel ボタンのついたメッセージダイアログを表示します
        /// </summary>
        /// <param name="text">表示するテキスト</param>
        /// <param name="caption">タイトルバーに表示するテキスト</param>
        /// <param name="icon">表示するアイコン</param>
        /// <returns>DialogResult.Yes DialogResult.No DialogResult.Cancel のいずれか</returns>
        public static DialogResult ShowYNC(string text, string caption, MessageDialogIcon icon)
        {
            return Show(text, caption, MessageDialogButtons.YesNoCancel, icon, MessageDialogDefaultButton.Button3);
        }
    }
}
