// hardcodet.net NotifyIcon for WPF
// Copyright (c) 2009 - 2013 Philipp Sumi
// Contact and Information: http://www.hardcodet.net
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Code Project Open License (CPOL);
// either version 1.0 of the License, or (at your option) any later
// version.
// 
// 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.
//
// THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Resources;
using System.Windows.Threading;
using Hardcodet.Wpf.TaskbarNotification.Interop;
namespace Hardcodet.Wpf.TaskbarNotification
{
    /// 
    /// Util and extension methods.
    /// 
    internal static class Util
    {
        public static readonly object SyncRoot = new object();
        #region IsDesignMode
        private static readonly bool isDesignMode;
        /// 
        /// Checks whether the application is currently in design mode.
        /// 
        public static bool IsDesignMode
        {
            get { return isDesignMode; }
        }
        #endregion
        #region construction
        static Util()
        {
            isDesignMode =
                (bool)
                    DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty,
                        typeof (FrameworkElement))
                        .Metadata.DefaultValue;
        }
        #endregion
        #region CreateHelperWindow
        /// 
        /// Creates an transparent window without dimension that
        /// can be used to temporarily obtain focus and/or
        /// be used as a window message sink.
        /// 
        /// Empty window.
        public static Window CreateHelperWindow()
        {
            return new Window
            {
                Width = 0,
                Height = 0,
                ShowInTaskbar = false,
                WindowStyle = WindowStyle.None,
                AllowsTransparency = true,
                Opacity = 0
            };
        }
        #endregion
        #region WriteIconData
        /// 
        /// Updates the taskbar icons with data provided by a given
        ///  instance.
        /// 
        /// Configuration settings for the NotifyIcon.
        /// Operation on the icon (e.g. delete the icon).
        /// True if the data was successfully written.
        /// See Shell_NotifyIcon documentation on MSDN for details.
        public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command)
        {
            return WriteIconData(ref data, command, data.ValidMembers);
        }
        /// 
        /// Updates the taskbar icons with data provided by a given
        ///  instance.
        /// 
        /// Configuration settings for the NotifyIcon.
        /// Operation on the icon (e.g. delete the icon).
        /// Defines which members of the 
        /// structure are set.
        /// True if the data was successfully written.
        /// See Shell_NotifyIcon documentation on MSDN for details.
        public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command, IconDataMembers flags)
        {
            //do nothing if in design mode
            if (IsDesignMode) return true;
            data.ValidMembers = flags;
            lock (SyncRoot)
            {
                return WinApi.Shell_NotifyIcon(command, ref data);
            }
        }
        #endregion
        #region GetBalloonFlag
        /// 
        /// Gets a  enum value that
        /// matches a given .
        /// 
        public static BalloonFlags GetBalloonFlag(this BalloonIcon icon)
        {
            switch (icon)
            {
                case BalloonIcon.None:
                    return BalloonFlags.None;
                case BalloonIcon.Info:
                    return BalloonFlags.Info;
                case BalloonIcon.Warning:
                    return BalloonFlags.Warning;
                case BalloonIcon.Error:
                    return BalloonFlags.Error;
                default:
                    throw new ArgumentOutOfRangeException("icon");
            }
        }
        #endregion
        #region ImageSource to Icon
        /// 
        /// Reads a given image resource into a WinForms icon.
        /// 
        /// Image source pointing to
        /// an icon file (*.ico).
        /// An icon object that can be used with the
        /// taskbar area.
        public static Icon ToIcon(this ImageSource imageSource)
        {
            if (imageSource == null) return null;
            Uri uri = new Uri(imageSource.ToString());
            StreamResourceInfo streamInfo = Application.GetResourceStream(uri);
            if (streamInfo == null)
            {
                string msg = "The supplied image source '{0}' could not be resolved.";
                msg = string.Format(msg, imageSource);
                throw new ArgumentException(msg);
            }
            return new Icon(streamInfo.Stream);
        }
        #endregion
        #region evaluate listings
        /// 
        /// Checks a list of candidates for equality to a given
        /// reference value.
        /// 
        /// 
        /// The evaluated value.
        /// A liste of possible values that are
        /// regarded valid.
        /// True if one of the submitted 
        /// matches the evaluated value. If the 
        /// parameter itself is null, too, the method returns false as well,
        /// which allows to check with null values, too.
        /// If 
        /// is a null reference.
        public static bool Is(this T value, params T[] candidates)
        {
            if (candidates == null) return false;
            foreach (var t in candidates)
            {
                if (value.Equals(t)) return true;
            }
            return false;
        }
        #endregion
        #region match MouseEvent to PopupActivation
        /// 
        /// Checks if a given  is a match for
        /// an effectively pressed mouse button.
        /// 
        public static bool IsMatch(this MouseEvent me, PopupActivationMode activationMode)
        {
            switch (activationMode)
            {
                case PopupActivationMode.LeftClick:
                    return me == MouseEvent.IconLeftMouseUp;
                case PopupActivationMode.RightClick:
                    return me == MouseEvent.IconRightMouseUp;
                case PopupActivationMode.LeftOrRightClick:
                    return me.Is(MouseEvent.IconLeftMouseUp, MouseEvent.IconRightMouseUp);
                case PopupActivationMode.LeftOrDoubleClick:
                    return me.Is(MouseEvent.IconLeftMouseUp, MouseEvent.IconDoubleClick);
                case PopupActivationMode.DoubleClick:
                    return me.Is(MouseEvent.IconDoubleClick);
                case PopupActivationMode.MiddleClick:
                    return me == MouseEvent.IconMiddleMouseUp;
                case PopupActivationMode.All:
                    //return true for everything except mouse movements
                    return me != MouseEvent.MouseMove;
                default:
                    throw new ArgumentOutOfRangeException("activationMode");
            }
        }
        #endregion
        #region execute command
        /// 
        /// Executes a given command if its  method
        /// indicates it can run.
        /// 
        /// The command to be executed, or a null reference.
        /// An optional parameter that is associated with
        /// the command.
        /// The target element on which to raise the command.
        public static void ExecuteIfEnabled(this ICommand command, object commandParameter, IInputElement target)
        {
            if (command == null) return;
            RoutedCommand rc = command as RoutedCommand;
            if (rc != null)
            {
                //routed commands work on a target
                if (rc.CanExecute(commandParameter, target)) rc.Execute(commandParameter, target);
            }
            else if (command.CanExecute(commandParameter))
            {
                command.Execute(commandParameter);
            }
        }
        #endregion
        /// 
        /// Returns a dispatcher for multi-threaded scenarios
        /// 
        /// Dispatcher
        internal static Dispatcher GetDispatcher(this DispatcherObject source)
        {
            //use the application's dispatcher by default
            if (Application.Current != null) return Application.Current.Dispatcher;
            //fallback for WinForms environments
            if (source.Dispatcher != null) return source.Dispatcher;
            // ultimately use the thread's dispatcher
            return Dispatcher.CurrentDispatcher;
        }
        /// 
        /// Checks whether the 
        ///  is bound or not.
        /// 
        /// The element to be checked.
        /// True if the data context property is being managed by a
        /// binding expression.
        /// If 
        /// is a null reference.
        public static bool IsDataContextDataBound(this FrameworkElement element)
        {
            if (element == null) throw new ArgumentNullException("element");
            return element.GetBindingExpression(FrameworkElement.DataContextProperty) != null;
        }
    }
}