/***************************************************************************
    copyright            : (C) 2013 by Lukas Krejci
    email                : krejclu6@fel.cvut.cz
 ***************************************************************************/

/***************************************************************************
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License version   *
 *   2.1 as published by the Free Software Foundation.                     *
 *                                                                         *
 *   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 GNU     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA         *
 *   02110-1301  USA                                                       *
 *                                                                         *
 *   Alternatively, this file is available under the Mozilla Public        *
 *   License Version 1.1.  You may obtain a copy of the License at         *
 *   http://www.mozilla.org/MPL/                                           *
 ***************************************************************************/

#ifndef TAGLIB_TABLEOFCONTENTSFRAME
#define TAGLIB_TABLEOFCONTENTSFRAME

#include "tbytevectorlist.h"
#include "id3v2tag.h"
#include "id3v2frame.h"

namespace TagLib {

  namespace ID3v2 {

    /*!
     * This is an implementation of ID3v2 table of contents frames.  Purpose
     * of this frame is to allow a table of contents to be defined.
     */

    //! An implementation of ID3v2 table of contents frames

    class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame
    {
      friend class FrameFactory;

    public:
      /*!
       * Creates a table of contents frame based on \a data.  \a tagHeader is
       * required as the internal frames are parsed based on the tag version.
       */
      TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data);

      /*!
       * Creates a table of contents frame with the element ID \a elementID,
       * the child elements \a children and embedded frames, which become owned
       * by this frame, in \a embeddedFrames.
       */
      TableOfContentsFrame(const ByteVector &elementID,
                           const ByteVectorList &children = ByteVectorList(),
                           const FrameList &embeddedFrames = FrameList());

      /*!
       * Destroys the frame.
       */
      ~TableOfContentsFrame() override;

      TableOfContentsFrame(const TableOfContentsFrame &) = delete;
      TableOfContentsFrame &operator=(const TableOfContentsFrame &) = delete;

      /*!
       * Returns the elementID of the frame.
       * Element ID is not intended to be human readable.
       *
       * \see setElementID()
       */
      ByteVector elementID() const;

      /*!
       * Returns \c true, if the frame is top-level (doesn't have
       * any parent CTOC frame).
       *
       * \see setIsTopLevel()
       */
      bool isTopLevel() const;

      /*!
       * Returns \c true, if the child elements list entries
       * are ordered.
       *
       * \see setIsOrdered()
       */
      bool isOrdered() const;

      /*!
       * Returns the count of child elements of the frame. It always
       * corresponds to the size of the child elements list.
       *
       * \see childElements()
       */
      unsigned int entryCount() const;

      /*!
       * Returns list of child elements of the frame.
       *
       * \see setChildElements()
       */
      ByteVectorList childElements() const;

      /*!
       * Sets the elementID of the frame to \a eID.
       *
       * \see elementID()
       */
      void setElementID(const ByteVector &eID);

      /*!
       * Sets, if the frame is top-level (doesn't have
       * any parent CTOC frame).
       *
       * \see isTopLevel()
       */
      void setIsTopLevel(const bool &t);

      /*!
       * Sets, if the child elements list entries
       * are ordered.
       *
       * \see isOrdered()
       */
      void setIsOrdered(const bool &o);

      /*!
       * Sets list of child elements of the frame to \a l.
       *
       * \see childElements()
       */
      void setChildElements(const ByteVectorList &l);

      /*!
       * Adds \a cE to the list of child elements of the frame.
       *
       * \see childElements()
       */
      void addChildElement(const ByteVector &cE);

      /*!
       * Removes \a cE to list of child elements of the frame.
       *
       * \see childElements()
       */
      void removeChildElement(const ByteVector &cE);

      /*!
       * Returns a reference to the frame list map.  This is a FrameListMap of
       * all of the frames embedded in the CTOC frame.
       *
       * This is the most convenient structure for accessing the CTOC frame's
       * embedded frames. Many frame types allow multiple instances of the same
       * frame type so this is a map of lists. In most cases however there will
       * only be a single frame of a certain type.
       *
       * \warning You should not modify this data structure directly, instead
       * use addEmbeddedFrame() and removeEmbeddedFrame().
       *
       * \see embeddedFrameList()
       */
      const FrameListMap &embeddedFrameListMap() const;

      /*!
       * Returns a reference to the embedded frame list.  This is a FrameList
       * of all of the frames embedded in the CTOC frame in the order that they
       * were parsed.
       *
       * This can be useful if for example you want to iterate over the CTOC frame's
       * embedded frames in the order that they occur in the CTOC frame.
       *
       * \warning You should not modify this data structure directly, instead
       * use addEmbeddedFrame() and removeEmbeddedFrame().
       */
      const FrameList &embeddedFrameList() const;

      /*!
       * Returns the embedded frame list for frames with the id \a frameID
       * or an empty list if there are no embedded frames of that type.  This
       * is just a convenience and is equivalent to:
       *
       * \code
       * embeddedFrameListMap()[frameID];
       * \endcode
       *
       * \see embeddedFrameListMap()
       */
      const FrameList &embeddedFrameList(const ByteVector &frameID) const;

      /*!
       * Add an embedded frame to the CTOC frame.  At this point the CTOC frame
       * takes ownership of the embedded frame and will handle freeing its memory.
       *
       * \note Using this method will invalidate any pointers on the list
       * returned by embeddedFrameList()
       */
      void addEmbeddedFrame(Frame *frame);

      /*!
       * Remove an embedded frame from the CTOC frame.  If \a del is \c true the frame's
       * memory will be freed; if it is \c false, it must be deleted by the user.
       *
       * \note Using this method will invalidate any pointers on the list
       * returned by embeddedFrameList()
       */
      void removeEmbeddedFrame(Frame *frame, bool del = true);

      /*!
       * Remove all embedded frames of type \a id from the CTOC frame and free their
       * memory.
       *
       * \note Using this method will invalidate any pointers on the list
       * returned by embeddedFrameList()
       */
      void removeEmbeddedFrames(const ByteVector &id);

      String toString() const override;

      PropertyMap asProperties() const override;

      /*!
       * CTOC frames each have a unique element ID. This searches for a CTOC
       * frame with the element ID \a eID and returns a pointer to it. This
       * can be used to link together parent and child CTOC frames.
       *
       * \see elementID()
       */
      static TableOfContentsFrame *findByElementID(const Tag *tag, const ByteVector &eID);

      /*!
       * CTOC frames each contain a flag that indicates, if CTOC frame is top-level (there isn't
       * any frame, which contains this frame in its child elements list). Only a single frame
       * within the tag can be top-level. This searches for a top-level CTOC frame.
       *
       * \see isTopLevel()
       */
      static TableOfContentsFrame *findTopLevel(const Tag *tag);

    protected:
      void parseFields(const ByteVector &data) override;
      ByteVector renderFields() const override;

    private:
      TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h);

      class TableOfContentsFramePrivate;
      TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
      std::unique_ptr<TableOfContentsFramePrivate> d;
    };
  }  // namespace ID3v2
}  // namespace TagLib

#endif
