/**
 * Copyright (C) 2002-2004 Michael Pyne <mpyne@kde.org>
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef TRACKSEQUENCEITERATOR_H
#define TRACKSEQUENCEITERATOR_H

#include "playlistitem.h"
#include "playlistsearch.h"

class Playlist;

/**
 * This abstract class defines an interface to be used by TrackSequenceManager,
 * to iterate over the items in a playlist.  Implement this class in a subclass
 * in order to define your own ordering for playlist sequences.  For an example,
 * see the UpcomingPlaylist class.
 *
 * @author Michael Pyne <mpyne@kde.org>
 * @see UpcomingPlaylist
 * @see TrackSequenceManager
 */
class TrackSequenceIterator
{
public:
    /**
     * Default constructor.
     */
    TrackSequenceIterator();

    /**
     * Default copy constructor.
     *
     * @param other the TrackSequenceIterator we are copying
     */
    TrackSequenceIterator(const TrackSequenceIterator & other);

    /**
     * Default destructor.
     */
    virtual ~TrackSequenceIterator();

    /**
     * This function moves the current item to the next track.  You must
     * reimplement this function in your subclasses.
     */
    virtual void advance() = 0;

    /**
     * This function moves the current item to the previous track.  This may
     * not always make sense, and the history functionality of the Playlist
     * class currently overrides this.  You must reimplement this function in
     * your subclass.
     */
    virtual void backup() = 0;

    /**
     * This function returns the current PlaylistItem, or 0 if the iterator is
     * not pointing at any PlaylistItem.
     *
     * @return current track
     */
    virtual PlaylistItem *current() const { return m_current; }

    /**
     * This function creates a perfect copy of the object it is called on, to
     * avoid the C++ slicing problem.  When you reimplement this function, you
     * should change the return type to the name of the subclass.
     *
     * @return pointer to a copy of the object
     */
    virtual TrackSequenceIterator *clone() const = 0;

    /**
     * This function is called by the TrackSequenceManager when current() returns
     * 0, if the TrackSequenceManager has a playlist defined.  This function
     * should choose an appropriate starting track and set it as the current
     * item.  This function must be reimplemented in subclasses.
     *
     * @param playlist the playlist to iterate over
     */
    virtual void prepareToPlay(Playlist *playlist) = 0;

    /**
     * This function is called whenever the current playlist changes, such as
     * having a new search applied, items added/removed, etc.  If you need to
     * update internal state, you should do so without affecting the current
     * playing item. Default implementation does nothing.
     */
    virtual void playlistChanged();

    /**
     * This function is called by the manager when \p item is about to be
     * removed.  Subclasses should ensure that they're not still holding a
     * pointer to the item.  The default implementation does nothing.
     *
     * @param item the item about to be removed.
     */
    virtual void itemAboutToDie(const PlaylistItem *item);

    /**
     * This function is called by the TrackSequenceManager is some situations,
     * such as when playback is being stopped.  If you subclass needs to reset
     * any internal data members, do so in this function.  This function must
     * be reimplemented in subclasses.
     */
    virtual void reset() = 0;

    /**
     * This function is a public mutator to set the current item.
     *
     * @param current the new current item
     */
    virtual void setCurrent(PlaylistItem *current);

private:
    PlaylistItem::Pointer m_current; ///< the current item
};

/**
 * This is the default iterator for JuK, supporting normal, random, and album
 * random playback with or without looping.
 *
 * @author Michael Pyne <mpyne@kde.org>
 */
class DefaultSequenceIterator : public TrackSequenceIterator
{
public:
    /**
     * Default constructor.
     */
    DefaultSequenceIterator();

    /**
     * Default copy constructor.
     *
     * @param other the DefaultSequenceIterator to copy.
     */
    DefaultSequenceIterator(const DefaultSequenceIterator &other);

    /**
     * Default destructor.
     */
    virtual ~DefaultSequenceIterator();

    /**
     * This function advances to the next item in the current sequence.  The
     * algorithm used depends on what playback mode is selected.
     */
    virtual void advance() override;

    /**
     * This function moves to the previous item in the playlist.  This occurs
     * no matter what playback mode is selected.
     */
    virtual void backup() override;

    /**
     * This function prepares the class for iterator.  If no random play mode
     * is selected, the first item in the given playlist is the starting item.
     * Otherwise, an item is randomly picked to be the starting item.
     *
     * @param playlist The playlist to initialize for.
     */
    virtual void prepareToPlay(Playlist *playlist) override;

    /**
     * This function clears all internal state, including any random play lists,
     * and what the current album is.
     */
    virtual void reset() override;

    /**
     * This function recalculates the random lists, and is should be called
     * whenever its current playlist changes (at least for searches).
     */
    virtual void playlistChanged() override;

    /**
     * Called when \p item is about to be removed.  This function ensures that
     * it isn't remaining in the random play list.
     */
    virtual void itemAboutToDie(const PlaylistItem *item) override;

    /**
     * This function sets the current item, and initializes any internal lists
     * that may be needed for playback.
     *
     * @param current The new current item.
     */
    virtual void setCurrent(PlaylistItem *current) override;

    /**
     * This function returns a perfect copy of the object it is called on, to
     * get around the C++ slicing problem.
     *
     * @return A copy of the object the method is called on.
     */
    virtual DefaultSequenceIterator *clone() const override;

private:

    /**
     * Reinitializes the internal random play list based on the playlist given
     * by \p p.  The currently playing item, if any, is automatically removed
     * from the list.
     *
     * @param p The Playlist to read items from.  If p is 0, the playlist of
     *        the currently playing item is used instead.
     */
    void refillRandomList(Playlist *p = 0);
    void initAlbumSearch(PlaylistItem *searchItem);

private:
    PlaylistItemList m_randomItems;
    PlaylistSearch m_albumSearch;
};

#endif /* TRACKSEQUENCEITERATOR_H */

// vim: set et sw=4 tw=0 sta:
