using System;
using System.ComponentModel;
using Csla.Properties;
namespace Csla
{
  /// 
  /// This is the base class from which most business objects
  /// will be derived.
  /// 
  /// 
  /// 
  /// This class is the core of the CSLA .NET framework. To create
  /// a business object, inherit from this class.
  /// 
  /// Please refer to 'Expert C# 2005 Business Objects' for
  /// full details on the use of this base class to create business
  /// objects.
  /// 
  /// 
  /// Type of the business object being defined.
  [Serializable()]
  public abstract class BusinessBase :
    Core.BusinessBase, Core.ISavable where T : BusinessBase
  {
    #region Object ID Value
    /// 
    /// Override this method to return a unique identifying
    /// value for this object.
    /// 
    /// 
    /// If you can not provide a unique identifying value, it
    /// is best if you can generate such a unique value (even
    /// temporarily). If you can not do that, then return 
    ///  and then manually override the
    /// ,  and
    ///  methods in your business object.
    /// 
    protected abstract object GetIdValue();
    #endregion
    #region System.Object Overrides
    /// 
    /// Compares this object for equality with another object, using
    /// the results of  to determine
    /// equality.
    /// 
    /// The object to be compared.
    public override bool Equals(object obj)
    {
      if (obj is T)
      {
        object id = GetIdValue();
        if (id == null)
          throw new ArgumentException(Resources.GetIdValueCantBeNull);
        return id.Equals(((T)obj).GetIdValue());
      }
      else
        return false;
    }
    /// 
    /// Returns a hash code value for this object, based on
    /// the results of .
    /// 
    public override int GetHashCode()
    {
      object id = GetIdValue();
      if (id == null)
        throw new ArgumentException(Resources.GetIdValueCantBeNull);
      return id.GetHashCode();
    }
    /// 
    /// Returns a text representation of this object by
    /// returning the  value
    /// in text form.
    /// 
    public override string ToString()
    {
      object id = GetIdValue();
      if (id == null)
        throw new ArgumentException(Resources.GetIdValueCantBeNull);
      return id.ToString();
    }
    #endregion
    #region Clone
    /// 
    /// Creates a clone of the object.
    /// 
    /// 
    /// A new object containing the exact data of the original object.
    /// 
    public T Clone()
    {
      return (T)GetClone();
    }
    #endregion
    #region Data Access
    /// 
    /// Saves the object to the database.
    /// 
    /// 
    /// 
    /// Calling this method starts the save operation, causing the object
    /// to be inserted, updated or deleted within the database based on the
    /// object's current state.
    /// 
    /// If  is 
    /// the object will be deleted. Otherwise, if  
    /// is  the object will be inserted. 
    /// Otherwise the object's data will be updated in the database.
    /// 
    /// All this is contingent on . If
    /// this value is , no data operation occurs. 
    /// It is also contingent on . 
    /// If this value is  an
    /// exception will be thrown to indicate that the UI attempted to save an
    /// invalid object.
    /// 
    /// It is important to note that this method returns a new version of the
    /// business object that contains any data updated during the save operation.
    /// You MUST update all object references to use this new version of the
    /// business object in order to have access to the correct object data.
    /// 
    /// You can override this method to add your own custom behaviors to the save
    /// operation. For instance, you may add some security checks to make sure
    /// the user can save the object. If all security checks pass, you would then
    /// invoke the base Save method via base.Save().
    /// 
    /// 
    /// A new object containing the saved values.
    public virtual T Save()
    {
      T result;
      if (this.IsChild)
        throw new NotSupportedException(Resources.NoSaveChildException);
      if (EditLevel > 0)
        throw new Validation.ValidationException(Resources.NoSaveEditingException);
      if (!IsValid)
        throw new Validation.ValidationException(Resources.NoSaveInvalidException);
      if (IsDirty)
        result = (T)DataPortal.Update(this);
      else
        result = (T)this;
      OnSaved(result);
      return result;
    }
    /// 
    /// Saves the object to the database, forcing
    /// IsNew to  and IsDirty to True.
    /// 
    /// 
    /// If , triggers overriding IsNew and IsDirty. 
    /// If  then it is the same as calling Save().
    /// 
    /// A new object containing the saved values.
    /// 
    /// This overload is designed for use in web applications
    /// when implementing the Update method in your 
    /// data wrapper object.
    /// 
    public T Save(bool forceUpdate)
    {
      if (forceUpdate && IsNew)
      {
        // mark the object as old - which makes it
        // not dirty
        MarkOld();
        // now mark the object as dirty so it can save
        MarkDirty(true);
      }
      return this.Save();
    }
    #endregion
    #region ISavable Members
    object Csla.Core.ISavable.Save()
    {
      return Save();
    }
    [NonSerialized]
    [NotUndoable]
    private EventHandler _nonSerializableSavedHandlers;
    [NotUndoable]
    private EventHandler _serializableSavedHandlers;
    
    /// 
    /// Event raised when an object has been saved.
    /// 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design",
      "CA1062:ValidateArgumentsOfPublicMethods")]
    public event EventHandler Saved
    {
      add
      {
        if (value.Method.IsPublic &&
           (value.Method.DeclaringType.IsSerializable ||
            value.Method.IsStatic))
          _serializableSavedHandlers = (EventHandler)
            System.Delegate.Combine(_serializableSavedHandlers, value);
        else
          _nonSerializableSavedHandlers = (EventHandler)
            System.Delegate.Combine(_nonSerializableSavedHandlers, value);
      }
      remove
      {
        if (value.Method.IsPublic &&
           (value.Method.DeclaringType.IsSerializable ||
            value.Method.IsStatic))
          _serializableSavedHandlers = (EventHandler)
            System.Delegate.Remove(_serializableSavedHandlers, value);
        else
          _nonSerializableSavedHandlers = (EventHandler)
            System.Delegate.Remove(_nonSerializableSavedHandlers, value);
      }
    }
    /// 
    /// Raises the Saved event, indicating that the
    /// object has been saved, and providing a reference
    /// to the new object instance.
    /// 
    /// The new object instance.
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void OnSaved(T newObject)
    {
      Csla.Core.SavedEventArgs args = new Csla.Core.SavedEventArgs(newObject);
      if (_nonSerializableSavedHandlers != null)
        _nonSerializableSavedHandlers.Invoke(this, args);
      if (_serializableSavedHandlers != null)
        _serializableSavedHandlers.Invoke(this, args);
    }
    #endregion
  }
}