using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.ComponentModel;
namespace Csla
{
  /// 
  /// This is the base class from which collections
  /// of editable root business objects should be
  /// derived.
  /// 
  /// 
  /// Type of editable root object to contain within
  /// the collection.
  /// 
  /// 
  /// 
  /// Your subclass should implement a factory method
  /// and should override or overload
  /// DataPortal_Fetch() to implement data retrieval.
  /// 
  /// Saving (inserts or updates) of items in the collection
  /// should be handled through the SaveItem() method on
  /// the collection. 
  /// 
  /// Removing an item from the collection
  /// through Remove() or RemoveAt() causes immediate deletion
  /// of the object, by calling the object's Delete() and
  /// Save() methods.
  /// 
  /// 
  [Serializable()]
  public abstract class EditableRootListBase : Core.ExtendedBindingList, Core.IParent
    where T : Core.IEditableBusinessObject, Core.ISavable
  {
    #region  SaveItem Methods
    private bool _activelySaving;
    /// 
    /// Saves the specified item in the list.
    /// 
    /// 
    /// Reference to the item to be saved.
    /// 
    /// 
    /// This method properly saves the child item,
    /// by making sure the item in the collection
    /// is properly replaced by the result of the
    /// Save() method call.
    /// 
    public void SaveItem(T item)
    {
      SaveItem(IndexOf(item));
    }
    /// 
    /// Saves the specified item in the list.
    /// 
    /// 
    /// Index of the item to be saved.
    /// 
    /// 
    /// This method properly saves the child item,
    /// by making sure the item in the collection
    /// is properly replaced by the result of the
    /// Save() method call.
    /// 
    public virtual void SaveItem(int index)
    {
      bool raisingEvents = this.RaiseListChangedEvents;
      this.RaiseListChangedEvents = false;
      _activelySaving = true;
      T item = this[index];
      int editLevel = item.EditLevel;
      // commit all changes
      for (int tmp = 1; tmp <= editLevel; tmp++)
        item.AcceptChanges();
      try
      {
        // do the save
        this[index] = (T)item.Save();
      }
      finally
      {
        // restore edit level to previous level
        for (int tmp = 1; tmp <= editLevel; tmp++)
          item.CopyState();
        _activelySaving = false;
        this.RaiseListChangedEvents = raisingEvents;
      }
      this.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));
    }
    #endregion
    #region  Insert, Remove, Clear
    /// 
    /// Gives the new object a parent reference to this
    /// list.
    /// 
    /// Index at which to insert the item.
    /// Item to insert.
    protected override void InsertItem(int index, T item)
    {
      item.SetParent(this);
      base.InsertItem(index, item);
    }
    /// 
    /// Removes an item from the list.
    /// 
    /// Index of the item
    /// to be removed.
    protected override void RemoveItem(int index)
    {
      // delete item from database
      T item = this[index];
      // only delete/save the item if it is not new
      if (!item.IsNew)
      {
        item.Delete();
        SaveItem(index);
      }
      // disconnect event handler if necessary
      System.ComponentModel.INotifyPropertyChanged c = item as System.ComponentModel.INotifyPropertyChanged;
      if (c != null)
      {
        c.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(Child_PropertyChanged);
      }
      base.RemoveItem(index);
    }
    #endregion
    #region  IParent Members
    void Csla.Core.IParent.ApplyEditChild(Core.IEditableBusinessObject child)
    {
      if (!_activelySaving && child.EditLevel == 0)
        SaveItem((T)child);
    }
    void Csla.Core.IParent.RemoveChild(Core.IEditableBusinessObject child)
    {
      // do nothing, removal of a child is handled by
      // the RemoveItem override
    }
    #endregion
    #region  Cascade Child events
    private void Child_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
      for (int index = 0; index < this.Count; index++)
      {
        if (ReferenceEquals(this[index], sender))
        {
          OnListChanged(new System.ComponentModel.ListChangedEventArgs(System.ComponentModel.ListChangedType.ItemChanged, index));
          break;
        }
      }
    }
    #endregion
    #region  Serialization Notification
    [OnDeserialized()]
    private void OnDeserializedHandler(StreamingContext context)
    {
      OnDeserialized(context);
      foreach (Core.IEditableBusinessObject child in this)
      {
        child.SetParent(this);
        System.ComponentModel.INotifyPropertyChanged c = child as System.ComponentModel.INotifyPropertyChanged;
        if (c != null)
        {
          c.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Child_PropertyChanged);
        }
      }
    }
    /// 
    /// This method is called on a newly deserialized object
    /// after deserialization is complete.
    /// 
    /// Serialization context object.
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnDeserialized(StreamingContext context)
    {
      // do nothing - this is here so a subclass
      // could override if needed
    }
    #endregion
    #region  Data Access
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "criteria")]
    private void DataPortal_Create(object criteria)
    {
      throw new NotSupportedException(Properties.Resources.CreateNotSupportedException);
    }
    /// 
    /// Override this method to allow retrieval of an existing business
    /// object based on data in the database.
    /// 
    /// An object containing criteria values to identify the object.
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
    protected virtual void DataPortal_Fetch(object criteria)
    {
      throw new NotSupportedException(Properties.Resources.FetchNotSupportedException);
    }
    private void DataPortal_Update()
    {
      throw new NotSupportedException(Properties.Resources.UpdateNotSupportedException);
    }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "criteria")]
    private void DataPortal_Delete(object criteria)
    {
      throw new NotSupportedException(Properties.Resources.DeleteNotSupportedException);
    }
    /// 
    /// Called by the server-side DataPortal prior to calling the 
    /// requested DataPortal_xyz method.
    /// 
    /// The DataPortalContext object passed to the DataPortal.
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member"), EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e)
    {
    }
    /// 
    /// Called by the server-side DataPortal after calling the 
    /// requested DataPortal_xyz method.
    /// 
    /// The DataPortalContext object passed to the DataPortal.
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member"), EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void DataPortal_OnDataPortalInvokeComplete(DataPortalEventArgs e)
    {
    }
    /// 
    /// Called by the server-side DataPortal if an exception
    /// occurs during data access.
    /// 
    /// The DataPortalContext object passed to the DataPortal.
    /// The Exception thrown during data access.
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member"), EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void DataPortal_OnDataPortalException(DataPortalEventArgs e, Exception ex)
    {
    }
    #endregion
  }
}