/*
    SPDX-FileCopyrightText: 2008 Hamish Rodda <rodda@kde.org>

    SPDX-License-Identifier: LGPL-2.0-only
*/

#ifndef KDEVPLATFORM_DUCHAINCHANGESET_H
#define KDEVPLATFORM_DUCHAINCHANGESET_H

#include <QVariant>

#include "../duchain/identifier.h"
#include "../duchain/topducontext.h"
#include "../duchain/declaration.h"

namespace KDevelop {
class DUChainChangeSet;
class DUChainChange;
class DUChainBase;
class DUContextRef;

template <typename AstNode>
class AstNodeRef;

/**
 * \short A reference to an existing read-only DUChain object.
 *
 * This class represents a duchain object (eg, a KDevelop::DUContext),
 * and allows changes to be planned for that object.
 *
 * \todo Evaluate usefulness of changing child contexts - needed?
 *
 * \warning you must not create cyclic references.
 * \author Hamish Rodda <rodda@kde.org>
 */
class KDEVPLATFORMLANGUAGE_EXPORT DUChainRef
{
    friend class DUChainChangeSet;

public:
    /*virtual ~DUChainRef();

       virtual const DUChainBase* object() const;
       virtual const DUContext* context() const;
       virtual const Declaration* declaration() const;

       virtual DUChainRef* objectRef() const;

       virtual DUChainBase* newObject() const;
       virtual DUContext* newContext() const;
       virtual Declaration* newDeclaration() const;

       const QList<DUChainChange*>& changes() const;

       /// Rename this object, if applicable
       void renameObject(const QualifiedIdentifier& newIdentifier);
       /// Change the access policy
       void setAccessPolicy(Declaration::AccessPolicy newPolicy);

       void deleteChildContext(DUContext* child);
       void insertChildContext(DUContextRef* newChild);

       void deleteDeclaration(Declaration* declaration);
       void insertDeclaration(Declaration* declaration, DUChainBase* afterObject);
       void appendDeclaration(Declaration* declaration);

       AbstractType::Ptr currentType() const;
       void changeType(AbstractType::Ptr newType);
     */
    /**
     * Rewrite the AST which created this duchain object. Eg:
     * - for declarations, the entire declaration.
     * - for contexts, the contents of the context.
     * - for types, the type declaration.
     *
     * \returns a reference to the AST which represents this object as it currently
     *          exists (after any existing duchain changes are applied).  Changes
     *          made to the AST will be applied along with the duchain change set.
     */
/*    template <typename AstNode>
    AstNodeRef<AstNode> * rewriteAst();

    /// Removes a change from this object reference, and deletes it.
    void deleteChange(DUChainChange* change);

   protected:
    /// Constructor.  Either takes an existing \a object (\a newObject = false), or a newly created \a object (\a newObject = true)
    DUChainRef(DUChainChangeSet* set, DUChainBase* object, bool newObject);
    /// Constructor.  Takes another object reference.
    DUChainRef(DUChainChangeSet* set, DUChainRef* original);

    /// Adds a change to this object reference. Takes ownership of the \a change.
    DUChainChange* addChange(DUChainChange* change);

   private:
    DUChainChangeSet* m_changeSet;
    DUChainBase* m_object;
    DUChainRef* m_objectRef;
    bool m_newObject;

    QList<DUChainChange*> m_changes;*/
};

using DUChainBaseList = QList<DUChainRef*>;

/**
 * \short Container class for a change to a duchain object.
 *
 * \author Hamish Rodda <rodda@kde.org>
 */
class KDEVPLATFORMLANGUAGE_EXPORT DUChainChange
{
public:
    enum ChangeTypes {
        Rename,
        ListInsert,
        ListRemove,
        ListClear,
        ItemReplace,
        ItemMove,
        TypeChange
    } type;

    explicit DUChainChange(ChangeTypes t) : type(t) {}

    enum ItemToChange {
        ContextChildren,
        ContextDeclarations
    } itemToChange;

    /// New local identifier (eg. for contexts, the new DUContext::localScopeIdentifier() )
    QualifiedIdentifier newIdentifier;

    /// The new object to occupy this position, if relevant
    DUChainRef* newObject;
    /// The list of objects to occupy this position, if relevant
    DUChainBaseList newList;
    /// The position to apply the object(s) in the list, if relevant
    int listOffset;
    /// The value of the position, if relevant
    QVariant newValue;

    AbstractType::Ptr newType;
};

/**
 * \short A set of changes to a DUChain.
 *
 * This class holds a set of all changes to a DU Chain, and provides an interface
 * to convenience functions provided by the specific language support involved.
 *
 * \author Hamish Rodda <rodda@kde.org>
 */
class KDEVPLATFORMLANGUAGE_EXPORT DUChainChangeSet
{
public:
    /**
     * Constructor.
     *
     * \param topContext the top context of the read-only DUChain to modify, or set to null if creating
     *                   a new DUChain from scratch.
     */
    explicit DUChainChangeSet(const ReferencedTopDUContext& topContext);

    /**
     * Destructor, deletes all objects, references and changes owned by this change set.
     */
    virtual ~DUChainChangeSet();

    /**
     * Create a new declaration to be managed by this change set.
     *
     * \returns the new declaration reference
     */
    virtual DUChainRef* newDeclaration() = 0;

    /**
     * Create a new class to be managed by this change set.
     *
     * \returns the new declaration reference
     */
    virtual DUChainRef* newClass() = 0;

    /**
     * Create a new function to be managed by this change set.
     *
     * \returns the new declaration reference
     */
    virtual DUChainRef* newFunction() = 0;

    /**
     * Copy an existing object from a change set.
     *
     * This change set takes ownership, so that
     * the new object will be deleted when the change set is no longer needed.
     *
     * \returns the new object reference
     */
    DUChainRef* copyRef(DUChainRef* ref);

    /**
     * Merge another changeset with this one. This changeset
     * takes ownership of all the objects in the other changeset.
     * After the merge, the merged object becomes empty.
     *
     * Both changesets must reference the same TopDuContext.
     */
    DUChainChangeSet& operator<<(DUChainChangeSet& rhs);

    /**
     * Produce a reference to an existing object in this chain, and replace the
     * object with the reference so that modifications to the reference are already
     * integrated into the change set.
     *
     * You may then modify this reference, and the modifications will be applied
     * to the chain when the change set is finalised.
     *
     * \returns a reference to \a source, which you may modify directly.
     */
    DUChainRef* modifyObject(DUChainBase* source);

    /**
     * Copy an existing object (whether from the DUChain or from the change set).
     * Does not insert the object into the chain.
     *
     * You may then modify this reference, and the modifications will be applied to the object when the change set is finalised.
     *
     * \returns a copy of \a source, which you may modify directly.
     */
    DUChainRef* copyObject(DUChainBase* source);

    /**
     * Retrieve the list of object references and changes.
     */
    QList<DUChainRef*> objectRefs() const;

    const ReferencedTopDUContext& topDuContext() const;

private:
    ReferencedTopDUContext m_topContext;

    QList<DUChainRef*> m_objectRefs;
};
}

#endif // KDEVPLATFORM_DUCHAINCHANGESET_H
