/* ----------------------------------------------------------------- */
/*                                                                   */
/*  Copyright (c) 2010-2013  hkrn                                    */
/*                                                                   */
/* All rights reserved.                                              */
/*                                                                   */
/* Redistribution and use in source and binary forms, with or        */
/* without modification, are permitted provided that the following   */
/* conditions are met:                                               */
/*                                                                   */
/* - Redistributions of source code must retain the above copyright  */
/*   notice, this list of conditions and the following disclaimer.   */
/* - Redistributions in binary form must reproduce the above         */
/*   copyright notice, this list of conditions and the following     */
/*   disclaimer in the documentation and/or other materials provided */
/*   with the distribution.                                          */
/* - Neither the name of the MMDAI project team nor the names of     */
/*   its contributors may be used to endorse or promote products     */
/*   derived from this software without specific prior written       */
/*   permission.                                                     */
/*                                                                   */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND            */
/* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,       */
/* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF          */
/* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE          */
/* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS */
/* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,          */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED   */
/* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     */
/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,   */
/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY    */
/* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
/* POSSIBILITY OF SUCH DAMAGE.                                       */
/* ----------------------------------------------------------------- */

#ifndef VPVM_MOTIONBASEMODEL_H
#define VPVM_MOTIONBASEMODEL_H

#include <vpvl2/IKeyframe.h>
#include <vpvl2/IModel.h>
#include <vpvl2/IMotion.h>
#include <vpvl2/qt/RenderContext.h> /* for IMotionSharedPtr */

#include <QString>
#include <QVariant>
#include <QAbstractItemView>
#include <QUndoStack>
#include <QUndoGroup>

namespace vpvl2 {
class IMotion;
}

namespace vpvm
{

using namespace vpvl2;
using namespace vpvl2::qt;

class MotionBaseModel : public QAbstractTableModel
{
    Q_OBJECT

public:
    class ITreeItem
    {
    public:
        virtual ~ITreeItem() {}
        virtual void addChild(ITreeItem *item) = 0;
        virtual ITreeItem *parent() const = 0;
        virtual ITreeItem *child(int row) const = 0;
        virtual const QString &name() const = 0;
        virtual bool isRoot() const = 0;
        virtual bool isCategory() const = 0;
        virtual int rowIndex() const = 0;
        virtual int countChildren() const = 0;
    };

    typedef QMap<QString, ITreeItem *> Keys;
    typedef QHash<QModelIndex, QVariant> Values;
    typedef QList<ITreeItem *> TreeItemList;
    typedef QSharedPointer<ITreeItem> RootPtr;
    typedef QSharedPointer<QUndoStack> UndoStackPtr;

    enum DataRole
    {
        kNameRole = 0x1000,
        kBinaryDataRole
    };
    static const int kTimeIndexColumnStep = 5;
    static const int kTimeIndexColumnMinimum = 31; /* 1 for header */

    static int toTimeIndex(const QModelIndex &index);
    static int toTimeIndex(int modelColumnIndex);
    static int toModelIndex(int timeIndex);

    MotionBaseModel(QUndoGroup *undo, QObject *parent = 0);
    virtual ~MotionBaseModel();

    QVariant headerData(int /* section */, Qt::Orientation /* orientation */, int /* role */) const;
    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
    QModelIndex parent(const QModelIndex &child) const;
    int rowCount(const QModelIndex &parent) const;
    void cutKeyframesByModelIndices(const QModelIndexList &indices, int timeIndex);
    QItemSelection selectKeyframesFromItemSelection(const QItemSelection &selection);

    virtual const QModelIndex timeIndexToModelIndex(ITreeItem *item, int timeIndex) const = 0;
    virtual const QString nameFromModelIndex(const QModelIndex &index) const = 0;
    virtual void saveMotion(IMotion *motion) = 0;
    virtual void copyKeyframesByModelIndices(const QModelIndexList &indices, int timeIndex) = 0;
    virtual void pasteKeyframesByTimeIndex(int timeIndex) = 0;
    virtual int maxTimeIndex() const = 0;
    virtual bool forceCameraUpdate() const = 0;
    virtual IMotionSharedPtr currentMotionRef() const { return m_motionRef; }

    void setTimeIndex(const IKeyframe::TimeIndex &newIndex);
    bool isModified() const { return m_modified; }
    int maxFrameCount() const { return m_timeIndexColumnOffset; }
    const IKeyframe::TimeIndex &timeIndex() const { return m_timeIndex; }
    bool canFetchMore(const QModelIndex & /* parent */) const;
    void fetchMore(const QModelIndex &parent);
    int timeIndexColumnMax() const;
    void setTimeIndexColumnMax(int newValue);
    void setTimeIndexColumnMax(const IMotionSharedPtr motion);
    void updateTimeIndexColumnMax();

public slots:
    virtual void removeMotion() = 0;
    virtual void addKeyframesByModelIndices(const QModelIndexList &indices) = 0;
    virtual void deleteKeyframesByModelIndices(const QModelIndexList &indices) = 0;

signals:
    void motionDidModify(bool value);
    void timeIndexDidChange(const IKeyframe::TimeIndex &newTimeIndex, const IKeyframe::TimeIndex &oldTimeIndex);
    void timeIndexColumnMaxDidChange(int newValue, int oldValue);
    void motionDidOpenProgress(const QString &title, bool value);
    void motionDidUpdateProgress(int value, int max, const QString &text);
    void motionDidLoad();

protected:
    virtual ITreeItem *rootRef() const = 0;
    void addUndoCommand(QUndoCommand *command);
    void resetModel();
    void setModified(bool value);

    IMotionSharedPtr m_motionRef;
    QUndoGroup *m_undoRef;
    IKeyframe::TimeIndex m_timeIndex;
    int m_timeIndexColumnMax;
    int m_timeIndexColumnOffset;
    bool m_modified;

private:
    void setTimeIndexColumnMax0(int newValue);

    Q_DISABLE_COPY(MotionBaseModel)
};

} /* namespace vpvl2 */

#endif // MOTIONBASEMODEL_H
