#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2013 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library 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
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.ComponentModel;
using System.Diagnostics;
#if !NO_LINQ_EXPRESSIONS
using System.Linq.Expressions;
#endif
using System.Reflection;
namespace Nuclex.Support {
  /// Base class for objects that support property change notifications
#if !NO_SERIALIZATION
  [Serializable]
#endif
  public abstract class Observable : INotifyPropertyChanged {
    /// Raised when a property of the instance has changed its value
#if !NO_SERIALIZATION
    [field: NonSerialized]
#endif
    public event PropertyChangedEventHandler PropertyChanged;
#if !NO_LINQ_EXPRESSIONS
    /// Triggers the PropertyChanged event for the specified property
    /// 
    ///   Lambda expression for the property that will be reported to have changed
    /// 
    /// 
    ///   
    ///     This notification should be fired post-change, i.e. when the property has
    ///     already changed its value.
    ///   
    ///   
    ///     
    ///       public int Limit {
    ///         get { return this.limit; }
    ///         set {
    ///           if(value != this.limit) {
    ///             this.limit = value;
    ///             OnPropertyChanged(() => Limit);
    ///           }
    ///         }
    ///       }
    ///     
    ///   
    /// 
    protected void OnPropertyChanged(Expression> property) {
      PropertyChangedEventHandler copy = PropertyChanged;
      if(copy != null) {
        copy(this, PropertyChangedEventArgsHelper.GetArgumentsFor(property));
      }
    }
#endif // !NO_LINQ_EXPRESSIONS
    /// Triggers the PropertyChanged event for the specified property
    /// Name of the property that has changed its value
    /// 
    ///   
    ///     This notification should be fired post-change, i.e. when the property has
    ///     already changed its value. If possible, use the other overload of this
    ///     method to ensure the property name will be updated during F2 refactoring.
    ///   
    ///   
    ///     
    ///       public int Limit {
    ///         get { return this.limit; }
    ///         set {
    ///           if(value != this.limit) {
    ///             this.limit = value;
    ///             OnPropertyChanged("Limit"); // Note: prefer lambda exp whenever possible
    ///           }
    ///         }
    ///       }
    ///     
    ///   
    /// 
    protected virtual void OnPropertyChanged(string propertyName) {
      enforceChangedPropertyExists(propertyName);
      PropertyChangedEventHandler copy = PropertyChanged;
      if(copy != null) {
        copy(this, PropertyChangedEventArgsHelper.GetArgumentsFor(propertyName));
      }
    }
    /// Ensures that a property with the specified name exists in the type
    /// Property name that will be checked
    [Conditional("DEBUG")]
    private void enforceChangedPropertyExists(string propertyName) {
      // An empty string or null indicates that all properties have changed
      if(string.IsNullOrEmpty(propertyName)) {
        return;
      }
      // Any other string needs to match a property name
      PropertyInfo property = GetType().GetProperty(propertyName);
      if(property == null) {
        throw new ArgumentException(
          string.Format(
            "Type '{0}' tried to raise a change notification for property '{1}', " +
            "but no such property exists!",
            GetType().Name, propertyName
          ),
          "propertyName"
        );
      }
    }
  }
} // namespace Nuclex.Support