namespace WPFWordControl.WindowsForms
{
    #region Using
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Windows.Forms;
    using Word = Microsoft.Office.Interop.Word;
    #endregion

    public partial class WordWindowsForms : UserControl
    {
        #region Свойства
        private Process process;
        private Word.Application word;
        private Word.Document document;
        private List<string> hiddenBars;
        private int originalWordWindowWidth;
        private int originalWordWindowHeight;
        private int originalWordWindowLeft;
        private int originalWordWindowTop;
        #endregion

        #region Конструктор
        public WordWindowsForms()
        {
            InitializeComponent();

            this.hiddenBars = new List<string>();

            this.wordContainer.Resize += OnContainerResize;
            System.Windows.Application.Current.MainWindow.Closing += OnMainWindowClosing;
            AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
        }
        #endregion

        #region Методы
        /// <summary>
        /// Запустить Word
        /// </summary>
        private void StartWord()
        {
            this.word = new Word.Application();
            this.word.Application.Caption = "WORD VIEWER";

            this.originalWordWindowWidth = this.word.Width;
            this.originalWordWindowHeight = this.word.Height;
            this.originalWordWindowLeft = this.word.Left;
            this.originalWordWindowTop = this.word.Top;

            this.word.Width = 0;
            this.word.Height = 0;
            this.word.Left = -100;
            this.word.Top = -100;

            this.word.Visible = true;
            this.word.CommandBars.ActiveMenuBar.Enabled = false;

            foreach (Process proc in Process.GetProcessesByName("winword"))
                if (proc.MainWindowTitle == "WORD VIEWER")
                    this.process = proc;

            SetParent(this.process.MainWindowHandle, this.wordContainer.Handle);
            ShowWindow(this.process.MainWindowHandle, SW_SHOWMAXIMIZED);
            MakeExternalWindowBorderless(this.process.MainWindowHandle);
            WindowsReStyle(this.process.MainWindowHandle);
            MoveWindow(this.process.MainWindowHandle, -3, -3, this.wordContainer.Width + 6, this.wordContainer.Height + 6, true);
        }

        /// <summary>
        /// Закрыть Word
        /// </summary>
        private void CloseWord()
        {
            if ((this.word != null))
            {
                if (this.word.Documents.Count > 0)
                    this.document.Close(SaveChanges: false);

                this.word.CommandBars.ActiveMenuBar.Enabled = true;

                foreach (string cb in this.hiddenBars)
                    this.word.CommandBars[cb].Visible = true;

                this.word.Width = this.originalWordWindowWidth;
                this.word.Height = this.originalWordWindowHeight;
                this.word.Left = this.originalWordWindowLeft;
                this.word.Top = this.originalWordWindowTop;

                this.word.Quit(SaveChanges: false);

                GC.Collect();
                GC.WaitForPendingFinalizers();

                Marshal.ReleaseComObject(this.document);
                Marshal.ReleaseComObject(this.word);
            }
        }
        #endregion

        #region Общие методы
        /// <summary>
        /// Открыть документ
        /// </summary>
        /// <param name="path">Путь к документу</param>
        public void OpenWordDocument(string path)
        {
            if (this.word == null)
                this.StartWord();

            foreach (Microsoft.Office.Core.CommandBar cb in this.word.CommandBars)
            {
                if (cb.Visible)
                {
                    try
                    {
                        if (!this.hiddenBars.Contains(cb.Name))
                            this.hiddenBars.Add(cb.Name);

                        cb.Visible = false;
                    }
                    catch { }
                }
            }

            if (this.word.Documents.Count > 0)
                this.document.Close(SaveChanges: false);

            this.document = this.word.Documents.Open(FileName: path, ReadOnly: true);
        }

        /// <summary>
        /// Закрывает текущую таблицу
        /// </summary>
        public void CloseWordDocument()
        {
            if (this.word != null && this.word.Documents.Count > 0)
                this.document.Close(SaveChanges: false);
        }

        /// <summary>
        /// Печатает текущий документ на принтере по умолчанию
        /// </summary>
        public void PrintWordDocument()
        {
            if (this.word != null && this.word.Documents.Count > 0)
                this.document.PrintOut();
        }
        #endregion

        #region События
        /// <summary>
        /// При изменении размеров родительского контейнера
        /// </summary>
        void OnContainerResize(object sender, EventArgs e)
        {
            Panel container = sender as Panel;

            if (container == null)
                return;

            if ((this.process != null))
            {
                MakeExternalWindowBorderless(this.process.MainWindowHandle);
                WindowsReStyle(this.process.MainWindowHandle);
                MoveWindow(this.process.MainWindowHandle, -3, -3, container.Width + 6, container.Height + 6, true);
            }
        }

        /// <summary>
        /// При закрытии приложения
        /// </summary>
        void OnMainWindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            this.CloseWord();
        }

        /// <summary>
        /// При краше приложения
        /// </summary>
        void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            this.CloseWord();
        }
        #endregion

        #region Not
        /// <summary>
        /// Замена оператора Not для Integer в VB.Net
        /// </summary>
        /// <param name="x">Число</param>
        public static int Not(int x)
        {
            byte[] b = BitConverter.GetBytes(x);

            b[0] = (byte)(byte.MaxValue - b[0]);
            b[1] = (byte)(byte.MaxValue - b[1]);
            b[2] = (byte)(byte.MaxValue - b[2]);
            b[3] = (byte)(byte.MaxValue - b[3]);

            return BitConverter.ToInt32(b, 0);
        }
        #endregion

        #region Изменить родителя окна
        [DllImport("user32.dll")]
        private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
        #endregion

        #region Перемещение окна
        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
        #endregion

        #region Развернуть окно на весь экран
        private const int SW_SHOWMAXIMIZED = 3;
        [DllImport("user32.dll")]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        #endregion

        #region Спрятать заголовок окна
        const int WS_BORDER = 8388608;
        const int WS_DLGFRAME = 4194304;
        const int WS_CAPTION = WS_BORDER | WS_DLGFRAME;
        const int WS_SYSMENU = 524288;
        const int WS_THICKFRAME = 262144;
        const int WS_MINIMIZE = 536870912;
        const int WS_MAXIMIZEBOX = 65536;
        const int GWL_STYLE = -16;
        const int GWL_EXSTYLE = -20;
        const int WS_EX_DLGMODALFRAME = 0x00000001;
        const int SWP_NOMOVE = 0x2;
        const int SWP_NOSIZE = 0x1;
        const int SWP_FRAMECHANGED = 0x20;
        const uint MF_BYPOSITION = 0x400;
        const uint MF_REMOVE = 0x1000;

        [DllImport("user32.dll")]
        public static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32.dll")]
        public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll")]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);

        public void MakeExternalWindowBorderless(IntPtr MainWindowHandle)
        {
            int Style = 0;
            Style = GetWindowLong(MainWindowHandle, GWL_STYLE);
            Style = Style & Not(WS_CAPTION);
            Style = Style & Not(WS_SYSMENU);
            Style = Style & Not(WS_THICKFRAME);
            Style = Style & Not(WS_MINIMIZE);
            Style = Style & Not(WS_MAXIMIZEBOX);
            SetWindowLong(MainWindowHandle, GWL_STYLE, Style);
            Style = GetWindowLong(MainWindowHandle, GWL_EXSTYLE);
            SetWindowLong(MainWindowHandle, GWL_EXSTYLE, Style | WS_EX_DLGMODALFRAME);
            //SetWindowPos(MainWindowHandle, new IntPtr(0), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
        }
        #endregion

        #region Спрятать меню окна
        [DllImport("user32.dll")]
        private static extern IntPtr GetMenu(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern int GetMenuItemCount(IntPtr hMenu);

        [DllImport("user32.dll")]
        private static extern bool DrawMenuBar(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern bool RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);

        public void WindowsReStyle(IntPtr MainWindowHandle)
        {
            int style = GetWindowLong(MainWindowHandle, GWL_STYLE);
            IntPtr HMENU = GetMenu(MainWindowHandle);
            int count = GetMenuItemCount(HMENU);

            if (count > 0)
            {
                for (int i = count; i >= 0; i += -1)
                {
                    uint menuToRemove = Convert.ToUInt32(i);

                    RemoveMenu(HMENU, menuToRemove, (MF_BYPOSITION | MF_REMOVE));
                }

                DrawMenuBar(MainWindowHandle);
            }
        }
        #endregion
    }
}
