using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;

using Foo.Platform;
using Foo.Platform.Win32;
using System.Diagnostics;
using System.Runtime.InteropServices;

////
// Ignore 'Ambiguity' warning message, seems like no way around those
////
#pragma warning disable 0467

namespace Foo.WorkspaceMgr.Closers
{
    class Closer_Office : IClose
    {

        #region IClose Members

        /// <summary>
        /// Dispatches the QueryClose Request to the Proper Office Closer
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType IQueryClose(string strArtifactLocation)
        {
            switch (Win32_WrapperFunc.GetRegisteredExeForFile(strArtifactLocation))
            {
                case "WINWORD.EXE":
                    return QueryClose_MSWord(strArtifactLocation);

                case "EXCEL.EXE":
                    return QueryClose_MSExcel(strArtifactLocation);

                case "POWERPNT.EXE":
                    return QueryClose_MSPowerPoint(strArtifactLocation);

                case "MSPUB.EXE":
                    return QueryClose_MSPublisher(strArtifactLocation);

                case "VISIO.EXE":
                    return QueryClose_MSVisio(strArtifactLocation);

                case "MSACCESS.EXE":
                    return QueryClose_MSAccess(strArtifactLocation);
            }                          
            return FuncDepBoolType.FunctionallityNotSupported;
        }

        /// <summary>
        /// Dispatches the Close Request to the Proper Office Closer
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType IClose(string strArtifactLocation)
        {
            switch (Win32_WrapperFunc.GetRegisteredExeForFile(strArtifactLocation))
            {
                case "WINWORD.EXE":
                    return Close_MSWord(strArtifactLocation);

                case "EXCEL.EXE":
                    return Close_MSExcel(strArtifactLocation);

                case "POWERPNT.EXE":
                    return Close_MSPowerPoint(strArtifactLocation);

                case "MSPUB.EXE":
                    return Close_MSPublisher(strArtifactLocation);

                case "VISIO.EXE":
                    return Close_MSVisio(strArtifactLocation);

                case "MSACCESS.EXE":
                    return Close_MSAccess(strArtifactLocation);
            }
            return FuncDepBoolType.FunctionallityNotSupported;            
        }

        #endregion

        #region Microsoft Word IClose Members

        public const string Word_ProgId = "Word.Application";

        /// <summary>
        /// To Do : Test This -  Let's us know if the user has unsaved data
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType QueryClose_MSWord(string strArtifactLocation)
        {  
            try
            {
                // Microsoft.Office.Interop.Word.Application app = null;
                // app = Win32Functions.GetCOMObject(Word_ProgId) as Microsoft.Office.Interop.Word.Application;
                var RunningWordDocuments = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.Word.Document>();
                foreach (Microsoft.Office.Interop.Word.Document doc in RunningWordDocuments)
                {
                    if (doc.FullName.ToLower() == strArtifactLocation.ToLower())
                    {                    
                        if (doc.Saved)
                            return FuncDepBoolType.Success;
                        else
                            return FuncDepBoolType.Failed;
                    }
                }
             }
             catch (Exception e)
             {
                string message = e.Message;
                message = message + "";
                // TODO: Log Something here
                return FuncDepBoolType.ErrorThrown;
             }

            //**** Another Way Stored here for Later ****
            // may be this is helpful..
            //using ComTypes = System.Runtime.InteropServices.ComTypes;
            //ComTypes.IPersistFile persistFile = (ComTypes.IPersistFile)document;
            //Checks an object for changes since it was last saved to its current file.
            //if (persistFile.IsDirty)
            //{
                //do your stuff
            //}
            return FuncDepBoolType.Failed;
        }

        /// <summary>
        /// *** Technically ** Here we want to close the Word Document (Not Prompt the user at all)
        /// **** we could close it with a WM_CLOSE or WM_SCLOSE or with the DOM.  ~for now it's the
        /// DOM, however more testing to be done here
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType Close_MSWord(string strArtifactLocation)
        {
            if (!String.IsNullOrEmpty(strArtifactLocation))
            {
                try
                {
                    var RunningWordDocuments = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.Word.Document>();
                    foreach (Microsoft.Office.Interop.Word.Document doc in RunningWordDocuments)
                    {
                        if (doc.FullName.ToLower() == strArtifactLocation.ToLower())
                        {
                            // Found the document to close * Close it Without Prompting *                    
                            object saveOption = Microsoft.Office.Interop.Word.WdSaveOptions.wdSaveChanges;
                            object originalFormat = Microsoft.Office.Interop.Word.WdOriginalFormat.wdOriginalDocumentFormat;
                            object routeDocument = false;
                            var app = doc.Application;                            

                            // Let's get the Window that belongs to this document
                            Microsoft.Office.Interop.Word.Window docWindow = null;
                            foreach (Microsoft.Office.Interop.Word.Window window in doc.Windows)
                            {
                                if (window.Document.FullName.ToLower() == strArtifactLocation.ToLower())
                                {
                                    docWindow = window;
                                }
                            }

                            //Object missing = Missing.Value;
                            doc.Close(ref saveOption, ref originalFormat, ref routeDocument);
                            Marshal.FinalReleaseComObject(doc); // force clean-up

                            // Close the Window
                            if (docWindow != null)
                                docWindow.Close(ref saveOption, ref routeDocument);

                            // Close Word if this is the Last Document Instance                        
                            if (app.Documents.Count == 0)
                            {
                                app.Quit(ref saveOption, ref originalFormat, ref routeDocument);
                                Marshal.FinalReleaseComObject(app); // force clean-up, closes Process for sure
                            }

                            return FuncDepBoolType.Success;
                        }
                    }
                    return FuncDepBoolType.Failed;
                }
                catch (Exception e)
                {
                    string message = e.Message;
                    message = message + "";
                    // TODO: Log Something here
                    return FuncDepBoolType.ErrorThrown;
                }
            }
            return FuncDepBoolType.ParametersInvalid;
        }        

        #endregion

        #region Microsoft Excel IClose Members

        public const string Excel_ProgId = "Excel.Application";
        
        /// <summary>
        /// To Do : Test This -  Let's us know if the user has unsaved data
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType QueryClose_MSExcel(string strArtifactLocation)
        {
            if (!String.IsNullOrEmpty(strArtifactLocation))
            {
                var RunningExcelWorkbooks = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.Excel.Workbook>();
                foreach (Microsoft.Office.Interop.Excel.Workbook book in RunningExcelWorkbooks)
                {
                    if (book.FullName.ToLower() == strArtifactLocation.ToLower())
                    {                        
                        if(book.Saved)
                           return FuncDepBoolType.Success;
                        else
                           return FuncDepBoolType.Failed;                        
                    }
                }
                return FuncDepBoolType.ArtifactLocationUnavailable;
            }           
            return FuncDepBoolType.ParametersInvalid;
        }

        /// <summary>
        /// *** Technically ** Here we want to close the Excel Workbook (Not Prompt the user at all)
        /// **** we could close it with a WM_CLOSE or WM_SCLOSE or with the DOM.  ~for now it's the
        /// DOM, however more testing to be done here
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType Close_MSExcel(string strArtifactLocation)
        {
            if (!String.IsNullOrEmpty(strArtifactLocation))
            {
                try
                {                    
                    var RunningExcelWorkbooks = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.Excel.Workbook>();                    
                    foreach (Microsoft.Office.Interop.Excel.Workbook book in RunningExcelWorkbooks)
                    {
                        if (book.FullName.ToLower() == strArtifactLocation.ToLower())
                        {                                                        
                            // In case there is a running rogue excel process left open, 
                            // we can always force it to close by closing the process
                            // int hWnd = app.Hwnd;
                            // Process process = Win32Functions.GetProcessFromHandle((IntPtr) hWnd);
                            var app = book.Application;

                            // Close the Workbook
                            book.Close(true, strArtifactLocation, false);
                            Marshal.FinalReleaseComObject(book); // force clean-up

                            // Close Excel if this is the last Workbook                            
                            if (app.Workbooks.Count == 0)
                            {
                                app.Quit();
                                Marshal.FinalReleaseComObject(app); // force clean-up, should clean up excel process
                                //process.Close();
                            }                            
                            return FuncDepBoolType.Success;
                        }
                    }
                    return FuncDepBoolType.ArtifactLocationUnavailable;
                }
                catch (Exception e)
                {
                    string message = e.Message;
                    message = message + "";
                    // TODO: Log Something here
                    return FuncDepBoolType.ErrorThrown;
                }
            }
            return FuncDepBoolType.ParametersInvalid;
        }

        #endregion

        #region Microsoft PowerPoint IClose Members

        public const string PowerPoint_ProgId = "PowerPoint.Application";

        /// <summary>
        /// To Do : Test This -  Let's us know if the user has unsaved data
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType QueryClose_MSPowerPoint(string strArtifactLocation)
        {                                        
            if (!String.IsNullOrEmpty(strArtifactLocation))
            {
                try
                {
                    //Microsoft.Office.Interop.PowerPoint.Application app = null;
                    //app = Win32Functions.GetCOMObject(PowerPoint_ProgId) as Microsoft.Office.Interop.PowerPoint.Application;
                    //foreach (Microsoft.Office.Interop.PowerPoint.Presentation presentation in app.Presentations)

                    var RunningPowerPoints = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.PowerPoint.Presentation>();
                    foreach (Microsoft.Office.Interop.PowerPoint.Presentation presentation in RunningPowerPoints)                
                    {
                        if (presentation.FullName.ToLower() == strArtifactLocation.ToLower())
                        {
                            switch (presentation.Saved)
                            {
                                case Microsoft.Office.Core.MsoTriState.msoTrue:
                                    return FuncDepBoolType.Success;

                                case Microsoft.Office.Core.MsoTriState.msoFalse:
                                    return FuncDepBoolType.Failed;
                            }
                        }
                    }                
                }
                catch (Exception e)
                {
                    string message = e.Message;
                    message = message + "";
                    // TODO: Log Something here   
                    return FuncDepBoolType.ErrorThrown;     
                }
            }
            return FuncDepBoolType.ParametersInvalid;            
        }

        /// <summary>
        /// *** Technically ** Here we want to close the Word Document (Not Prompt the user at all)
        /// **** we could close it with a WM_CLOSE or WM_SCLOSE or with the DOM.  ~for now it's the
        /// DOM, however more testing to be done here
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType Close_MSPowerPoint(string strArtifactLocation)
        {
            if (!String.IsNullOrEmpty(strArtifactLocation))
            {
                try
                {
                    //Microsoft.Office.Interop.PowerPoint.Application app = null;
                    //app = Win32Functions.GetCOMObject(PowerPoint_ProgId) as Microsoft.Office.Interop.PowerPoint.Application;
                    //foreach (Microsoft.Office.Interop.PowerPoint.Presentation presentation in app.Presentations)

                    var RunningPowerPoints = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.PowerPoint.Presentation>();
                    foreach (Microsoft.Office.Interop.PowerPoint.Presentation presentation in RunningPowerPoints)
                    {
                        if (presentation.FullName.ToLower() == strArtifactLocation.ToLower())
                        {
                            var app = presentation.Application;

                            // Save this Presentation
                            presentation.Save();

                            // Close this Presentation
                            presentation.Close();

                            Marshal.FinalReleaseComObject(presentation); // force clean-up

                            // Close PowerPoint if there are no more Presentations                        
                            if (app.Presentations.Count == 0)
                            {
                                app.Quit();
                                Marshal.FinalReleaseComObject(app); // force clean-up, kill application for sure
                            }

                            return FuncDepBoolType.Success;
                        }
                    }
                    return FuncDepBoolType.Failed;

                }
                catch (Exception e)
                {
                    string message = e.Message;
                    message = message + "";
                    // TODO: Log Something here
                    return FuncDepBoolType.ErrorThrown;
                }
            }
            return FuncDepBoolType.ParametersInvalid;
        }

        #endregion

        #region Microsoft Publisher IClose Members

        public const string Publisher_ProgId = "Publisher.Application";

        /// <summary>
        /// To Do : Test This -  Let's us know if the user has unsaved data
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType QueryClose_MSPublisher(string strArtifactLocation)
        {            
            var RunningPublisherApps = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.Publisher.Application>();
            foreach (Microsoft.Office.Interop.Publisher.Application app in RunningPublisherApps)
            {
                foreach (Microsoft.Office.Interop.Publisher.Document pubDoc in app.Documents)           
                {
                    if (pubDoc.FullName.ToLower() == strArtifactLocation.ToLower())
                    {
                        try
                        {
                            if (pubDoc.Saved)
                                return FuncDepBoolType.Success;
                            else
                                return FuncDepBoolType.Failed;
                        }
                        catch (Exception e)
                        {
                            string message = e.Message;
                            message = message + "";
                            // TODO: Log Something here                        
                        }

                        break;
                    }
                }
            }         
            return FuncDepBoolType.Success;
        }

        /// <summary>
        /// *** Technically ** Here we want to close the Word Document (Not Prompt the user at all)
        /// **** we could close it with a WM_CLOSE or WM_SCLOSE or with the DOM.  ~for now it's the
        /// DOM, however more testing to be done here
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType Close_MSPublisher(string strArtifactLocation)
        {
                        
            IntPtr hWnd = IntPtr.Zero;            
            try
            {
                var RunningPublisherApps = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.Publisher.Application>();            
                foreach (Microsoft.Office.Interop.Publisher.Application app in RunningPublisherApps)
                {
                    foreach (Microsoft.Office.Interop.Publisher.Document pubDoc in app.Documents)
                    {
                        if (pubDoc.FullName.ToLower() == strArtifactLocation.ToLower())
                        {
                            // Save the Document
                            pubDoc.Save();

                            // Get the Handle
                            hWnd = (IntPtr)pubDoc.ActiveWindow.Hwnd;
                            goto FOUND_DOCUMENT_HWND;
                        }
                    }
                }
                return FuncDepBoolType.Failed;
            }
            catch (Exception e)
            {
                string message = e.Message;
                message = message + "";
                // TODO: Log Something here                        
            }

            FOUND_DOCUMENT_HWND:
            // Get the Process
            Process process = Win32Functions.GetProcessFromHandle(hWnd);                        

            // Send Close Message
            if (Win32Functions.SendMessage((IntPtr)hWnd, (int)WindowMessage.WM_CLOSE, IntPtr.Zero, IntPtr.Zero) == 0)
            {
                process.Kill();
                return FuncDepBoolType.Success;
            }
            else
            {
                return FuncDepBoolType.Failed;
            }                                              
        }

        #endregion

        #region Microsoft Visio IClose Members

        public const string Visio_ProgId = "Visio.Application";

        /// <summary>
        /// To Do : Test This -  Let's us know if the user has unsaved data
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType QueryClose_MSVisio(string strArtifactLocation)
        {
            
            var RunningVisioDocs = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.Visio.Document>();
            foreach (Microsoft.Office.Interop.Visio.Document doc in RunningVisioDocs)
            {
                if (doc.FullName.ToLower() == strArtifactLocation.ToLower())
                {
                    try
                    {
                        if (doc.Saved)
                            return FuncDepBoolType.Success;
                        else
                            return FuncDepBoolType.Failed;
                    }
                    catch (Exception e)
                    {
                        string message = e.Message;
                        message = message + "";
                        // TODO: Log Something here                        
                    }

                    break;
                }
            }
            
            return FuncDepBoolType.Success;
        }

        /// <summary>
        /// *** Technically ** Here we want to close the Word Document (Not Prompt the user at all)
        /// **** we could close it with a WM_CLOSE or WM_SCLOSE or with the DOM.  ~for now it's the
        /// DOM, however more testing to be done here
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType Close_MSVisio(string strArtifactLocation)
        {

            var RunningVisioDocs = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.Visio.Document>();
            foreach (Microsoft.Office.Interop.Visio.Document doc in RunningVisioDocs)
            {
                if (doc.FullName.ToLower() == strArtifactLocation.ToLower())
                {
                    try
                    {
                        doc.Save();

                        var app = doc.Application;

                        doc.Close();
                        Marshal.FinalReleaseComObject(doc); // Force Clean-up

                        if (app.Documents.Count == 0)
                        {
                            app.Quit();
                            Marshal.FinalReleaseComObject(app); // force clean-up
                        }

                        
                        return FuncDepBoolType.Success;                        
                    }
                    catch (Exception e)
                    {
                        string message = e.Message;
                        message = message + "";
                        // TODO: Log Something here                        
                    }
                }
            }

            return FuncDepBoolType.Success;
        }

        #endregion

        #region Microsoft Access IClose Members

        public const string Access_ProgId = "Access.Application";

        /// <summary>
        /// To Do : Test This -  Let's us know if the user has unsaved data
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType QueryClose_MSAccess(string strArtifactLocation)
        {

            var RunningAccessApps = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.Access.Application>();                        
            foreach (Microsoft.Office.Interop.Access.Application app in RunningAccessApps)
            {
                if (app.CurrentProject.FullName.ToLower() == strArtifactLocation.ToLower())
                {
                    return FuncDepBoolType.Success;
                }                
            }
            return FuncDepBoolType.Failed;                       
        }

        /// <summary>
        /// *** Technically ** Here we want to close the Word Document (Not Prompt the user at all)
        /// **** we could close it with a WM_CLOSE or WM_SCLOSE or with the DOM.  ~for now it's the
        /// DOM, however more testing to be done here
        /// </summary>
        /// <param name="strArtifactLocation"></param>
        /// <returns></returns>
        public FuncDepBoolType Close_MSAccess(string strArtifactLocation)
        {

            var RunningAccessApps = Win32Functions.GetRunningObjectsOfType<Microsoft.Office.Interop.Access.Application>();
            foreach (Microsoft.Office.Interop.Access.Application app in RunningAccessApps)
            {
                if (app.CurrentProject.FullName.ToLower() == strArtifactLocation.ToLower())
                {
                    // Get Handle and Process
                    IntPtr hWnd = (IntPtr) app.hWndAccessApp();
                    Process process = Win32Functions.GetProcessFromHandle((IntPtr)hWnd);

                    // Close the database
                    //app.CloseCurrentDatabase();
                    app.Quit(Microsoft.Office.Interop.Access.AcQuitOption.acQuitSaveAll);
                    Marshal.FinalReleaseComObject(app); // force clean-up
                    
                    // Kill the process                    
                    process.Kill();

                    return FuncDepBoolType.Success;
                }
            }
            return FuncDepBoolType.Failed;
        }

        #endregion
    }
}
