// 
// ApplicationEvents.cs
//  
// Author:
//       Michael Hutchinson <mhutchinson@novell.com>
// 
// Copyright (c) 2010 Novell, Inc. (http://www.novell.com)
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;

namespace MonoDevelop.MacInterop
{
    public static class ApplicationEvents
    {
        static object lockObj = new object ();
        
        #region Quit
        
        static EventHandler<ApplicationQuitEventArgs> quit;
        static IntPtr quitHandlerRef = IntPtr.Zero;
        
        public static event EventHandler<ApplicationQuitEventArgs> Quit {
            add {
                lock (lockObj) {
                    quit += value;
                    if (quitHandlerRef == IntPtr.Zero)
                        quitHandlerRef = Carbon.InstallApplicationEventHandler (HandleQuit, CarbonEventApple.QuitApplication);
                }
            }
            remove {
                lock (lockObj) {
                    quit -= value;
                    if (quit == null && quitHandlerRef != IntPtr.Zero) {
                        Carbon.RemoveEventHandler (quitHandlerRef);
                        quitHandlerRef = IntPtr.Zero;
                    }
                }
            }
        }
        
        static CarbonEventHandlerStatus HandleQuit (IntPtr callRef, IntPtr eventRef, IntPtr user_data)
        {
            var args = new ApplicationQuitEventArgs ();
            quit (null, args);
            return args.UserCancelled? CarbonEventHandlerStatus.UserCancelled : args.HandledStatus;
        }
        
        #endregion
        
        #region Reopen
        
        static EventHandler<ApplicationEventArgs> reopen;
        static IntPtr reopenHandlerRef = IntPtr.Zero;
        
        public static event EventHandler<ApplicationEventArgs> Reopen {
            add {
                lock (lockObj) {
                    reopen += value;
                    if (reopenHandlerRef == IntPtr.Zero)
                        reopenHandlerRef = Carbon.InstallApplicationEventHandler (HandleReopen, CarbonEventApple.ReopenApplication);
                }
            }
            remove {
                lock (lockObj) {
                    reopen -= value;
                    if (reopen == null && reopenHandlerRef != IntPtr.Zero) {
                        Carbon.RemoveEventHandler (reopenHandlerRef);
                        reopenHandlerRef = IntPtr.Zero;
                    }
                }
            }
        }
        
        static CarbonEventHandlerStatus HandleReopen (IntPtr callRef, IntPtr eventRef, IntPtr user_data)
        {
            var args = new ApplicationEventArgs ();
            reopen (null, args);
            return args.HandledStatus;
        }
        
        #endregion
        
        #region OpenDocuments
        
        static EventHandler<ApplicationDocumentEventArgs> openDocuments;
        static IntPtr openDocumentsHandlerRef = IntPtr.Zero;
        
        public static event EventHandler<ApplicationDocumentEventArgs> OpenDocuments {
            add {
                lock (lockObj) {
                    openDocuments += value;
                    if (openDocumentsHandlerRef == IntPtr.Zero)
                        openDocumentsHandlerRef = Carbon.InstallApplicationEventHandler (HandleOpenDocuments, CarbonEventApple.OpenDocuments);
                }
            }
            remove {
                lock (lockObj) {
                    openDocuments -= value;
                    if (openDocuments == null && openDocumentsHandlerRef != IntPtr.Zero) {
                        Carbon.RemoveEventHandler (openDocumentsHandlerRef);
                        openDocumentsHandlerRef = IntPtr.Zero;
                    }
                }
            }
        }
        
        static CarbonEventHandlerStatus HandleOpenDocuments (IntPtr callRef, IntPtr eventRef, IntPtr user_data)
        {
            try {
                var docs = Carbon.GetFileListFromEventRef (eventRef);
                var args = new ApplicationDocumentEventArgs (docs);
                openDocuments (null, args);
                return args.HandledStatus;
            } catch (Exception ex) {
                System.Console.WriteLine (ex);
                return CarbonEventHandlerStatus.NotHandled;
            }
        }
        
        #endregion
        
        #region OpenUrls
        
        static EventHandler<ApplicationUrlEventArgs> openUrls;
        static IntPtr openUrlsHandlerRef = IntPtr.Zero;
        
        public static event EventHandler<ApplicationUrlEventArgs> OpenUrls {
            add {
                lock (lockObj) {
                    openUrls += value;
                    if (openUrlsHandlerRef == IntPtr.Zero)
                        openUrlsHandlerRef = Carbon.InstallApplicationEventHandler (HandleOpenUrls,
                            new CarbonEventTypeSpec[] {
                                //For some reason GetUrl doesn't take CarbonEventClass.AppleEvent
                                //need to use GURL, GURL
                                new CarbonEventTypeSpec (CarbonEventClass.Internet, (int)CarbonEventApple.GetUrl)
                            }
                        );
                }
            }
            remove {
                lock (lockObj) {
                    openUrls -= value;
                    if (openUrls == null && openUrlsHandlerRef != IntPtr.Zero) {
                        Carbon.RemoveEventHandler (openUrlsHandlerRef);
                        openUrlsHandlerRef = IntPtr.Zero;
                    }
                }
            }
        }
        
        static CarbonEventHandlerStatus HandleOpenUrls (IntPtr callRef, IntPtr eventRef, IntPtr user_data)
        {
            try {
                var urls = Carbon.GetUrlListFromEventRef (eventRef);
                var args = new ApplicationUrlEventArgs (urls);
                openUrls (null, args);
                return args.HandledStatus;
            } catch (Exception ex) {
                System.Console.WriteLine (ex);
                return CarbonEventHandlerStatus.NotHandled;
            }
        }
        
        #endregion
    }
    
    public class ApplicationEventArgs : EventArgs
    {
        public bool Handled { get; set; }
        
        internal CarbonEventHandlerStatus HandledStatus {
            get {
                return Handled? CarbonEventHandlerStatus.Handled : CarbonEventHandlerStatus.NotHandled;
            }
        }
    }
    
    public class ApplicationQuitEventArgs : ApplicationEventArgs
    {
        public bool UserCancelled { get; set; }
    }
    
    public class ApplicationDocumentEventArgs : ApplicationEventArgs
    {
        public ApplicationDocumentEventArgs (IDictionary<string,int> documents)
        {
            this.Documents = documents;
        }       
        
        public IDictionary<string,int> Documents { get; private set; }
    }
    
    public class ApplicationUrlEventArgs : ApplicationEventArgs
    {
        public ApplicationUrlEventArgs (IList<string> urls)
        {
            this.Urls = urls;
        }       
        
        public IList<string> Urls { get; private set; }
    }
}
