// File:        NCollection_Sequence.hxx
// Created:     28.03.02 20:41:43
// Author:      Alexander GRIGORIEV
// Copyright:   Open Cascade 2002

#ifndef NCollection_Sequence_HeaderFile
#define NCollection_Sequence_HeaderFile

#include <NCollection_BaseCollection.hxx>
#include <NCollection_BaseSequence.hxx>

#ifndef No_Exception
#include <Standard_OutOfRange.hxx>
#include <Standard_NoSuchObject.hxx>
#endif

#ifdef WNT
// Disable the warning "operator new unmatched by delete"
#pragma warning (push)
#pragma warning (disable:4291)
#endif

/**
 * Purpose:     Definition of a sequence of elements indexed by
 *              an Integer in range of 1..n
 */              
template <class TheItemType> class NCollection_Sequence
  : public NCollection_BaseCollection<TheItemType>,
    public NCollection_BaseSequence
{

 public:
  //!   Class defining sequence node - for internal use by Sequence
  class Node : public NCollection_SeqNode
  {
  public:
    //! Constructor
    Node (const TheItemType& theItem) :
      NCollection_SeqNode ()
      { myValue = theItem; }
    //! Constant value access
    const TheItemType& Value () const { return myValue; }
    //! Variable value access
    TheItemType&       ChangeValue () { return myValue; }
    //! Memory allocation
    void* operator new(size_t theSize,
                       const Handle(NCollection_BaseAllocator)& theAllocator) 
    { return theAllocator->Allocate(theSize); }

  private:
    TheItemType    myValue;
  }; // End of nested class Node

 public:
  //!   Implementation of the Iterator interface.
  class Iterator : public NCollection_BaseCollection<TheItemType>::Iterator,
                   public NCollection_BaseSequence::Iterator
  {
  public:
    //! Empty constructor - for later Init
    Iterator  (void) {}
    //! Constructor with initialisation
    Iterator  (const NCollection_Sequence& theSeq,
               const Standard_Boolean      isStart = Standard_True)
      : NCollection_BaseSequence::Iterator (theSeq, isStart) {}
    //! Assignment
    Iterator& operator= (const Iterator& theIt)
    {
      NCollection_BaseSequence::Iterator& me = * this;
      me.operator= (theIt);
      return * this;
    }
    //! Check end
    virtual Standard_Boolean More (void) const
    { return (myCurrent!=NULL); }
    //! Make step
    virtual void Next (void)         
    { if (myCurrent) myCurrent = (NCollection_SeqNode *) myCurrent->Next(); }
    //! Constant value access
    virtual const TheItemType& Value (void) const
    { return ((const Node *)myCurrent)->Value(); }
    //! Variable value access
    virtual TheItemType& ChangeValue (void) const
    { return ((Node *)myCurrent)->ChangeValue(); }
    //! Operator new for allocating iterators
    void* operator new(size_t theSize,
                       const Handle(NCollection_BaseAllocator)& theAllocator) 
    { return theAllocator->Allocate(theSize); }
  }; // End of nested class Iterator

 public:
  // ---------- PUBLIC METHODS ------------

  //! Constructor
  NCollection_Sequence(const Handle(NCollection_BaseAllocator)& theAllocator=0L)
    : NCollection_BaseCollection<TheItemType>(theAllocator),
      NCollection_BaseSequence() {}

  //! Copy constructor
  NCollection_Sequence (const NCollection_Sequence& theOther) :
    NCollection_BaseCollection<TheItemType>(theOther.myAllocator),
    NCollection_BaseSequence()
  { *this = theOther; }

  //! Number of items
  virtual Standard_Integer Size (void) const
  { return mySize; }

  //! Number of items
  Standard_Integer Length (void) const
  { return mySize; }

  //! Empty query
  Standard_Boolean IsEmpty (void) const
  { return (mySize==0); }

  //! Reverse sequence
  void Reverse (void)
  { PReverse(); }

  //! Exchange two members
  void Exchange (const Standard_Integer I,
                 const Standard_Integer J )
  { PExchange(I, J); }

  //! Static deleter to be passed to BaseSequence
  static void delNode (NCollection_SeqNode * theNode, 
                       Handle(NCollection_BaseAllocator)& theAl)
  {
    ((Node *) theNode)->~Node();
    theAl->Free(theNode);
  }

  //! Clear the items out
  void Clear (void)
  { ClearSeq (delNode, this->myAllocator); }
  
  //! Replace this sequence by the items of theOther
  NCollection_Sequence& operator= (const NCollection_Sequence& theOther)
  { 
    Assign (theOther);
    return * this;
  }

  //! Replace this sequence by the items of theOther collection
  virtual void Assign (const NCollection_BaseCollection<TheItemType>& theOther)
  {
    if (this == &theOther)
      return;
    Clear();
    TYPENAME NCollection_BaseCollection<TheItemType>::Iterator& anIter = 
      theOther.CreateIterator();
    for (; anIter.More(); anIter.Next())
      Append(anIter.Value());
  }

  //! Remove one item
  void Remove (Iterator& thePosition)
  { RemoveSeq (thePosition, delNode, this->myAllocator); }

  //! Remove one item
  void Remove (const Standard_Integer theIndex)
  { RemoveSeq (theIndex, delNode, this->myAllocator); }

  //! Remove range of items
  void Remove (const Standard_Integer theFromIndex,
               const Standard_Integer theToIndex)
  { RemoveSeq (theFromIndex, theToIndex, delNode, this->myAllocator); }

  //! Append one item
  void Append (const TheItemType& theItem)
  { PAppend (new (this->myAllocator) Node (theItem)); }

  //! Append another sequence (making it empty)
  void Append (NCollection_Sequence& theSeq)
  {
    if (myFirstItem == theSeq.myFirstItem) Assign (theSeq);
    PAppend (theSeq);
  }

  //! Prepend one item
  void Prepend (const TheItemType& theItem)
  { PPrepend (new (this->myAllocator) Node (theItem)); }

  //! Prepend another sequence (making it empty)
  void Prepend (NCollection_Sequence& theSeq)
  {
    if (myFirstItem == theSeq.myFirstItem) Assign (theSeq);
    PPrepend (theSeq);
  }

  //! InsertBefore theIndex theItem
  void InsertBefore (const Standard_Integer theIndex, 
                     const TheItemType&     theItem)
  { InsertAfter (theIndex-1, theItem); }

  //! InsertBefore theIndex another sequence
  void InsertBefore (const Standard_Integer theIndex,
                     NCollection_Sequence&  theSeq)
  { InsertAfter (theIndex-1, theSeq); }
  
  //! InsertAfter the position of iterator
  void InsertAfter  (Iterator&              thePosition,
                     const TheItemType&     theItem)
  { PInsertAfter (thePosition, new (this->myAllocator) Node (theItem)); }

  //! InsertAfter theIndex theItem
  void InsertAfter  (const Standard_Integer theIndex,
                     NCollection_Sequence&  theSeq)
  { PInsertAfter (theIndex, theSeq); }

  //! InsertAfter theIndex another sequence
  void InsertAfter (const Standard_Integer  theIndex, 
                    const TheItemType&      theItem)
  {
#if !defined No_Exception && !defined No_Standard_OutOfRange
    if (theIndex < 0 || theIndex > mySize)
      Standard_OutOfRange::Raise ("NCollection_Sequence::InsertAfter");
#endif
    PInsertAfter (theIndex, new (this->myAllocator) Node (theItem));
  }

  //! Split in two sequences
  void Split (const Standard_Integer theIndex, NCollection_Sequence& theSeq)
  { theSeq.Clear(); PSplit (theIndex, theSeq); }

  //! First item access
  const TheItemType& First () const
  {
#if !defined No_Exception && !defined No_Standard_NoSuchObject
    if (mySize == 0)
      Standard_NoSuchObject::Raise ("NCollection_Sequence::First");
#endif
    return ((const Node *) myFirstItem) -> Value();
  }

  //! Last item access
  const TheItemType& Last () const
  {
#if !defined No_Exception && !defined No_Standard_NoSuchObject
    if (mySize == 0)
      Standard_NoSuchObject::Raise ("NCollection_Sequence::Last");
#endif
    return ((const Node *) myLastItem) -> Value();
  }

  //! Constant item access by theIndex
  const TheItemType& Value (const Standard_Integer theIndex) const
  {
#if !defined No_Exception && !defined No_Standard_OutOfRange
    if (theIndex <= 0 || theIndex > mySize)
      Standard_OutOfRange::Raise ("NCollection_Sequence::Value");
#endif
    NCollection_Sequence * const aLocalTHIS = (NCollection_Sequence *) this;
    aLocalTHIS -> myCurrentItem  = Find (theIndex);
    aLocalTHIS -> myCurrentIndex = theIndex;
    return ((const Node *) myCurrentItem) -> Value();
  }

  //! Constant operator()
  const TheItemType& operator() (const Standard_Integer theIndex) const
  { return Value(theIndex); }

  //! Variable item access by theIndex
  TheItemType& ChangeValue (const Standard_Integer theIndex)
  {
#if !defined No_Exception && !defined No_Standard_OutOfRange
    if (theIndex <= 0 || theIndex > mySize)
      Standard_OutOfRange::Raise ("NCollection_Sequence::ChangeValue");
#endif
    myCurrentItem  = Find (theIndex);
    myCurrentIndex = theIndex;
    return ((Node *) myCurrentItem) -> ChangeValue();
  }

  //! Variable operator()
  TheItemType& operator() (const Standard_Integer theIndex)
  { return ChangeValue(theIndex); }

  //! Set item value by theIndex
  void SetValue (const Standard_Integer theIndex, 
                 const TheItemType& theItem)
  { ChangeValue (theIndex) = theItem; }

  // ******** Destructor - clears the Sequence
  ~NCollection_Sequence (void)
  { Clear(); }


 private:
  // ----------- PRIVATE METHODS -----------

  //! Creates Iterator for use on BaseCollection
  virtual TYPENAME NCollection_BaseCollection<TheItemType>::Iterator& 
    CreateIterator(void) const
  { return *(new (this->IterAllocator()) Iterator(*this)); }

  // ---------- FRIEND CLASSES ------------
  friend class Iterator;

};

#ifdef WNT
#pragma warning (pop)
#endif

#endif
