// edbee - Copyright (c) 2012-2025 by Rick Blommers and contributors
// SPDX-License-Identifier: MIT

#pragma once

#include "edbee/exports.h"

#include "edbee/models/change.h"

namespace edbee {

class AbstractRangedChange;
class LineDataListChange;
class TextChange;
class TextRangeSet;

/// A special mergable group textchange. Used by the editor  to merge editing operation together..
/// I hate the name, but currently don't know a better name of this class :)
///
/// This is a complete rewrite of the earlier implementation. This implemenation
/// simply adds all changes to a group. It tries to compress the changes that are compressable
/// This group has 1 previous selection and 1 next selection. All selection changes are 'removed' and only the last and first state is stored.
class EDBEE_EXPORT MergableChangeGroup : public ChangeGroup
{
public:
    MergableChangeGroup(TextEditorController* controller);
    virtual ~MergableChangeGroup();

    // this change cannot be optimized away
    virtual bool isDiscardable();
    virtual void groupClosed();

    virtual void execute(TextDocument* document);
    virtual void revert(TextDocument* document);

private:
    void addOffsetDeltaToChanges(QList<AbstractRangedChange*>& changes, size_t fromIndex, ptrdiff_t delta);
    qsizetype findInsertIndexForOffset(QList<AbstractRangedChange*>& changes, size_t offset);
    qsizetype mergeChange(QList<AbstractRangedChange*>& changes, TextDocument* doc, AbstractRangedChange* newChange, ptrdiff_t& delta);
    void inverseMergeRemainingOverlappingChanges(QList<AbstractRangedChange*>& changes, TextDocument* doc, qsizetype mergedAtIndex, size_t orgStartOffset, size_t orgEndOffset, ptrdiff_t delta);

    void giveChangeToList(QList<AbstractRangedChange*>& changes, TextDocument* doc, AbstractRangedChange* change);
    void giveAndMergeChangeToList(QList<AbstractRangedChange*>& changes, TextDocument* doc, AbstractRangedChange* change);

public:
    void giveSingleTextChange(TextDocument* doc, TextChange* change);
    void giveLineDataListTextChange(TextDocument* doc, LineDataListChange* change);

    virtual void giveChange(TextDocument* doc, Change* change);
    virtual Change* at(size_t idx);
    virtual Change* take(size_t idx);
    virtual size_t size();
    virtual void clear(bool performDelete=true);

    /// This method tries to merge the given change with the other change
    /// The textChange supplied with this method should NOT have been executed yet.
    /// It's the choice of this merge operation if the execution is required
    /// @param textChange the textchange to merge
    /// @return true if the merge has been successfull. False if nothing has been merged and executed
    virtual bool giveAndMerge(TextDocument* document, Change* textChange);

    virtual QString toString();
    QString toSingleTextChangeTestString();

    void moveChangesFromGroup(TextDocument* doc, ChangeGroup* group);

protected:

    bool mergeAsGroup(TextDocument* document, Change* textChange);
    bool mergeAsSelection(TextDocument* document, Change* textChange);

    void compressTextChanges(TextDocument* document);
    void compressChanges(TextDocument* document);

private:

    QList<AbstractRangedChange*> textChangeList_;           ///< The list of textchanges
    QList<LineDataListChange*> lineDataTextChangeList_;     ///<The list with liendata text changes
    QList<Change*> miscChangeList_;                         ///< Other textchanges

    TextRangeSet* previousSelection_;
    TextRangeSet* newSelection_;
};

} // edbee
