// Scintilla source code edit control
/** @file Selection.h
 ** Classes maintaining the selection.
 **/
// Copyright 2009 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#ifndef SELECTION_H
#define SELECTION_H

#ifdef SCI_NAMESPACE
namespace Scintilla {
#endif

class SelectionPosition {
    int position;
    int virtualSpace;
public:
    explicit SelectionPosition(int position_=INVALID_POSITION, int virtualSpace_=0) : position(position_), virtualSpace(virtualSpace_) {
        PLATFORM_ASSERT(virtualSpace < 800000);
        if (virtualSpace < 0)
            virtualSpace = 0;
    }
    void Reset() {
        position = 0;
        virtualSpace = 0;
    }
    void MoveForInsertDelete(bool insertion, int startChange, int length);
    bool operator ==(const SelectionPosition &other) const {
        return position == other.position && virtualSpace == other.virtualSpace;
    }
    bool operator <(const SelectionPosition &other) const;
    bool operator >(const SelectionPosition &other) const;
    bool operator <=(const SelectionPosition &other) const;
    bool operator >=(const SelectionPosition &other) const;
    int Position() const {
        return position;
    }
    void SetPosition(int position_) {
        position = position_;
        virtualSpace = 0;
    }
    int VirtualSpace() const {
        return virtualSpace;
    }
    void SetVirtualSpace(int virtualSpace_) {
        PLATFORM_ASSERT(virtualSpace_ < 800000);
        if (virtualSpace_ >= 0)
            virtualSpace = virtualSpace_;
    }
    void Add(int increment) {
        position = position + increment;
    }
    bool IsValid() const {
        return position >= 0;
    }
};

// Ordered range to make drawing simpler
struct SelectionSegment {
    SelectionPosition start;
    SelectionPosition end;
    SelectionSegment() : start(), end() {
    }
    SelectionSegment(SelectionPosition a, SelectionPosition b) {
        if (a < b) {
            start = a;
            end = b;
        } else {
            start = b;
            end = a;
        }
    }
    bool Empty() const {
        return start == end;
    }
    void Extend(SelectionPosition p) {
        if (start > p)
            start = p;
        if (end < p)
            end = p;
    }
};

struct SelectionRange {
    SelectionPosition caret;
    SelectionPosition anchor;

    SelectionRange() : caret(), anchor() {
    }
    SelectionRange(SelectionPosition single) : caret(single), anchor(single) {
    }
    SelectionRange(int single) : caret(single), anchor(single) {
    }
    SelectionRange(SelectionPosition caret_, SelectionPosition anchor_) : caret(caret_), anchor(anchor_) {
    }
    SelectionRange(int caret_, int anchor_) : caret(caret_), anchor(anchor_) {
    }
    bool Empty() const {
        return anchor == caret;
    }
    int Length() const;
    // int Width() const;   // Like Length but takes virtual space into account
    bool operator ==(const SelectionRange &other) const {
        return caret == other.caret && anchor == other.anchor;
    }
    bool operator <(const SelectionRange &other) const {
        return caret < other.caret || ((caret == other.caret) && (anchor < other.anchor));
    }
    void Reset() {
        anchor.Reset();
        caret.Reset();
    }
    void ClearVirtualSpace() {
        anchor.SetVirtualSpace(0);
        caret.SetVirtualSpace(0);
    }
    bool Contains(int pos) const;
    bool Contains(SelectionPosition sp) const;
    bool ContainsCharacter(int posCharacter) const;
    SelectionSegment Intersect(SelectionSegment check) const;
    SelectionPosition Start() const {
        return (anchor < caret) ? anchor : caret;
    }
    SelectionPosition End() const {
        return (anchor < caret) ? caret : anchor;
    }
    bool Trim(SelectionRange range);
    // If range is all virtual collapse to start of virtual space
    void MinimizeVirtualSpace();
};

class Selection {
    std::vector<SelectionRange> ranges;
    std::vector<SelectionRange> rangesSaved;
    SelectionRange rangeRectangular;
    size_t mainRange;
    bool moveExtends;
    bool tentativeMain;
public:
    enum selTypes { noSel, selStream, selRectangle, selLines, selThin };
    selTypes selType;

    Selection();
    ~Selection();
    bool IsRectangular() const;
    int MainCaret() const;
    int MainAnchor() const;
    SelectionRange &Rectangular();
    SelectionSegment Limits() const;
    // This is for when you want to move the caret in response to a
    // user direction command - for rectangular selections, use the range
    // that covers all selected text otherwise return the main selection.
    SelectionSegment LimitsForRectangularElseMain() const;
    size_t Count() const;
    size_t Main() const;
    void SetMain(size_t r);
    SelectionRange &Range(size_t r);
    SelectionRange &RangeMain();
    bool MoveExtends() const;
    void SetMoveExtends(bool moveExtends_);
    bool Empty() const;
    SelectionPosition Last() const;
    int Length() const;
    void MovePositions(bool insertion, int startChange, int length);
    void TrimSelection(SelectionRange range);
    void SetSelection(SelectionRange range);
    void AddSelection(SelectionRange range);
    void AddSelectionWithoutTrim(SelectionRange range);
    void TentativeSelection(SelectionRange range);
    void CommitTentative();
    int CharacterInSelection(int posCharacter) const;
    int InSelectionForEOL(int pos) const;
    int VirtualSpaceFor(int pos) const;
    void Clear();
    void RemoveDuplicates();
    void RotateMain();
    bool Tentative() const { return tentativeMain; }
    std::vector<SelectionRange> RangesCopy() const {
        return ranges;
    }
};

#ifdef SCI_NAMESPACE
}
#endif

#endif
