// Copyright (C) 2014 panacoran <panacoran@users.sourceforge.jp>
// 
// This program is part of Protra.
//
// Protra is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
// 
// $Id$

// 以下のコードはstackoverflowのHans Passant氏の回答を参考に実装した。
// http://stackoverflow.com/questions/2576156/
// 氏からは回答のコードを任意に扱ってよいと許諾を得ている。

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace Protra.Lib
{
    /// <summary>
    /// MessageBoxを親の中央に表示するためのクラス
    /// </summary>
    public class CenteredDialogHelper : IDisposable
    {
        private readonly Control _control;
        private int _repeat = 10;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public CenteredDialogHelper()
        {
            if (Application.OpenForms.Count == 0)
                return;
            _control = Application.OpenForms[0];
            _control.BeginInvoke(new MethodInvoker(FindDialog));
        }

        private void FindDialog()
        {
            if (_repeat == 0)
                return;
            if (EnumThreadWindows(GetCurrentThreadId(), EnumWindows, IntPtr.Zero) && --_repeat > 0)
                _control.BeginInvoke(new MethodInvoker(FindDialog));
        }

        private bool EnumWindows(IntPtr handle, IntPtr param)
        {
            var sb = new StringBuilder(256);
            GetClassName(handle, sb, sb.Capacity);
            if (sb.ToString() != "#32770") // dialog box class
                return true;
            Rect prect;
            var phandle = handle;
            do
            {
                phandle = GetWindow(phandle, 2); // GW_HWNDNEXT
                if (phandle == IntPtr.Zero)
                    return false;
                GetWindowRect(phandle, out prect);
            } while (prect.Right - prect.Left == 0); // 大きさ0のtooltips_class32の場合があるので、さらに下のWindowを取得する。
            Rect crect;
            GetWindowRect(handle, out crect);
            var width = crect.Right - crect.Left;
            var height = crect.Bottom - crect.Top;
            MoveWindow(handle, prect.Left + (prect.Right - prect.Left - width) / 2,
                       prect.Top + (prect.Bottom - prect.Top - height) / 2,
                       width, height, true);
            return false;
        }

        /// <summary>
        /// ダイアログボックスを探すのをやめる。
        /// </summary>
        public void Dispose()
        {
            _repeat = 0;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct Rect
        {
// ReSharper disable FieldCanBeMadeReadOnly.Local
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
// ReSharper restore FieldCanBeMadeReadOnly.Local
        }

        private delegate bool EnumThreadWndProc(IntPtr handle, IntPtr param);

        [DllImport("user32.dll")]
        private static extern bool EnumThreadWindows(UInt32 threadId, EnumThreadWndProc handler, IntPtr param);

        [DllImport("kernel32.dll")]
        private static extern UInt32 GetCurrentThreadId();

        [DllImport("user32.dll")]
        private static extern bool GetClassName(IntPtr handle, StringBuilder name, int size);

        [DllImport("user32.dll")]
        private static extern IntPtr GetWindow(IntPtr handle, UInt32 cmd);

        [DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr handle, out Rect rect);

        [DllImport("user32.dll")]
        private static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool repaint);
    }
}