/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#pragma once

#include <memory>
#include <queue>
#include <tuple>
#include <vector>
#include <optional>

#include <com/sun/star/text/WrapTextMode.hpp>
#include <com/sun/star/io/WrongFormatException.hpp>
#include <oox/mathml/importutils.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>
#include <tools/color.hxx>
#include <tools/long.hxx>

#include <rtftok/RTFDocument.hxx>
#include "rtfreferencetable.hxx"
#include "rtfsprm.hxx"
#include "rtflistener.hxx"

class SvStream;
namespace oox
{
class GraphicHelper;
}
namespace com::sun::star
{
namespace beans
{
class XPropertySet;
}
namespace document
{
class XDocumentProperties;
}
namespace lang
{
class XMultiServiceFactory;
}
}

namespace writerfilter::rtftok
{
class RTFParserState;
class RTFDocumentImpl;
class RTFTokenizer;
class RTFSdrImport;
class TableRowBuffer;

enum class RTFBorderState
{
    NONE,
    PARAGRAPH,
    PARAGRAPH_BOX,
    CELL,
    PAGE,
    CHARACTER
};

/// Different kind of buffers for table cell contents.
enum class RTFBufferTypes
{
    SetStyle,
    /// Stores properties, should be created only in bufferProperties().
    Props,
    PropsChar,
    NestRow,
    CellEnd,
    StartRun,
    Text,
    UText,
    EndRun,
    PAR,
    StartShape,
    /// Imports a shape.
    ResolveShape,
    EndShape,
    ResolveSubstream,
    /// Restores RTFParserState::aPicture.
    Picture
};

/// Form field types
enum class RTFFormFieldType
{
    NONE,
    TEXT,
    CHECKBOX,
    LIST
};

enum class RTFBmpStyle
{
    NONE,
    PNG,
    JPEG,
    DIBITMAP
};

enum class RTFFieldStatus
{
    NONE,
    INSTRUCTION,
    RESULT
};

/// A buffer storing dmapper calls.
using Buf_t = std::tuple<RTFBufferTypes, RTFValue::Pointer_t, tools::SvRef<TableRowBuffer>>;
using RTFBuffer_t = std::deque<Buf_t>;

/// holds one nested table row
class TableRowBuffer : public virtual SvRefBase
{
    RTFBuffer_t m_aBuffer;
    ::std::deque<RTFSprms> m_aCellsSprms;
    ::std::deque<RTFSprms> m_aCellsAttributes;
    int m_nCells;
    writerfilter::Reference<Properties>::Pointer_t m_pParaProperties;
    writerfilter::Reference<Properties>::Pointer_t m_pFrameProperties;
    writerfilter::Reference<Properties>::Pointer_t m_pRowProperties;

public:
    TableRowBuffer(RTFBuffer_t aBuffer, std::deque<RTFSprms> aSprms,
                   std::deque<RTFSprms> aAttributes, int const nCells)
        : m_aBuffer(std::move(aBuffer))
        , m_aCellsSprms(std::move(aSprms))
        , m_aCellsAttributes(std::move(aAttributes))
        , m_nCells(nCells)
    {
    }

    RTFBuffer_t& GetBuffer() { return m_aBuffer; }
    std::deque<RTFSprms>& GetCellsSprms() { return m_aCellsSprms; }
    std::deque<RTFSprms>& GetCellsAttributes() { return m_aCellsAttributes; }
    int GetCells() const { return m_nCells; }
    writerfilter::Reference<Properties>::Pointer_t& GetParaProperties()
    {
        return m_pParaProperties;
    }
    writerfilter::Reference<Properties>::Pointer_t& GetFrameProperties()
    {
        return m_pFrameProperties;
    }
    writerfilter::Reference<Properties>::Pointer_t& GetRowProperties() { return m_pRowProperties; }
};

/// An entry in the color table.
class RTFColorTableEntry
{
public:
    void SetRed(sal_uInt8 nRed)
    {
        m_bAuto = false;
        m_nR = nRed;
    }
    void SetGreen(sal_uInt8 nGreen)
    {
        m_bAuto = false;
        m_nG = nGreen;
    }
    void SetBlue(sal_uInt8 nBlue)
    {
        m_bAuto = false;
        m_nB = nBlue;
    }
    Color GetColor() const { return m_bAuto ? COL_AUTO : Color(m_nR, m_nG, m_nB); }

private:
    bool m_bAuto = true;
    sal_uInt8 m_nR = 0;
    sal_uInt8 m_nG = 0;
    sal_uInt8 m_nB = 0;
};

/// Stores the properties of a shape.
class RTFShape : public virtual SvRefBase
{
public:
    RTFShape();

    std::vector<std::pair<OUString, OUString>>& getProperties() { return m_aProperties; }

    const std::vector<std::pair<OUString, OUString>>& getProperties() const
    {
        return m_aProperties;
    }

    std::vector<std::pair<OUString, OUString>>& getGroupProperties() { return m_aGroupProperties; }

    void setLeft(sal_Int32 nLeft) { m_nLeft = nLeft; }

    sal_Int32 getLeft() const { return m_nLeft; }

    void setTop(sal_Int32 nTop) { m_nTop = nTop; }

    sal_Int32 getTop() const { return m_nTop; }

    void setRight(sal_Int32 nRight) { m_nRight = nRight; }

    sal_Int32 getRight() const { return m_nRight; }

    void setBottom(sal_Int32 nBottom) { m_nBottom = nBottom; }

    sal_Int32 getBottom() const { return m_nBottom; }

    void setZ(sal_Int32 nZ) { m_oZ = nZ; }

    bool hasZ() const { return bool(m_oZ); }

    sal_Int32 getZ() const { return *m_oZ; }

    void setHoriOrientRelation(sal_Int16 nHoriOrientRelation)
    {
        m_nHoriOrientRelation = nHoriOrientRelation;
    }

    sal_Int16 getHoriOrientRelation() const { return m_nHoriOrientRelation; }

    void setVertOrientRelation(sal_Int16 nVertOrientRelation)
    {
        m_nVertOrientRelation = nVertOrientRelation;
    }

    sal_Int16 getVertOrientRelation() const { return m_nVertOrientRelation; }

    void setHoriOrientRelationToken(sal_uInt32 nHoriOrientRelationToken)
    {
        m_nHoriOrientRelationToken = nHoriOrientRelationToken;
    }

    sal_uInt32 getHoriOrientRelationToken() const { return m_nHoriOrientRelationToken; }

    void setVertOrientRelationToken(sal_uInt32 nVertOrientRelationToken)
    {
        m_nVertOrientRelationToken = nVertOrientRelationToken;
    }

    sal_uInt32 getVertOrientRelationToken() const { return m_nVertOrientRelationToken; }

    void setWrap(css::text::WrapTextMode nWrap) { m_nWrap = nWrap; }

    css::text::WrapTextMode getWrap() const { return m_nWrap; }

    void setInBackground(bool bInBackground) { m_bInBackground = bInBackground; }

    bool getInBackground() const { return m_bInBackground; }

    RTFSprms& getWrapPolygonSprms() { return m_aWrapPolygonSprms; }

    RTFSprms& getAnchorAttributes() { return m_aAnchorAttributes; }

    std::pair<Id, RTFValue::Pointer_t>& getWrapSprm() { return m_aWrapSprm; }

private:
    std::vector<std::pair<OUString, OUString>> m_aProperties; ///< Properties of a single shape.
    std::vector<std::pair<OUString, OUString>>
        m_aGroupProperties; ///< Properties applied on the groupshape.
    sal_Int32 m_nLeft = 0;
    sal_Int32 m_nTop = 0;
    sal_Int32 m_nRight = 0;
    sal_Int32 m_nBottom = 0;
    std::optional<sal_Int32> m_oZ; ///< Z-Order of the shape.
    sal_Int16 m_nHoriOrientRelation
        = 0; ///< Horizontal text::RelOrientation for drawinglayer shapes.
    sal_Int16 m_nVertOrientRelation = 0; ///< Vertical text::RelOrientation for drawinglayer shapes.
    sal_uInt32 m_nHoriOrientRelationToken = 0; ///< Horizontal dmapper token for Writer pictures.
    sal_uInt32 m_nVertOrientRelationToken = 0; ///< Vertical dmapper token for Writer pictures.
    css::text::WrapTextMode m_nWrap = css::text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE;
    /// If shape is below text (true) or text is below shape (false).
    bool m_bInBackground = false;
    /// Wrap polygon, written by RTFSdrImport::resolve(), read by RTFDocumentImpl::resolvePict().
    RTFSprms m_aWrapPolygonSprms;
    /// Anchor attributes like wrap distance, written by RTFSdrImport::resolve(), read by RTFDocumentImpl::resolvePict().
    RTFSprms m_aAnchorAttributes;
    /// Wrap type, written by RTFDocumentImpl::popState(), read by RTFDocumentImpl::resolvePict().
    std::pair<Id, RTFValue::Pointer_t> m_aWrapSprm{ 0, nullptr };
};

/// Stores the properties of a drawing object.
class RTFDrawingObject : public RTFShape
{
public:
    RTFDrawingObject();

    void setShape(const css::uno::Reference<css::drawing::XShape>& xShape) { m_xShape = xShape; }
    const css::uno::Reference<css::drawing::XShape>& getShape() const { return m_xShape; }
    void setPropertySet(const css::uno::Reference<css::beans::XPropertySet>& xPropertySet)
    {
        m_xPropertySet = xPropertySet;
    }
    const css::uno::Reference<css::beans::XPropertySet>& getPropertySet() const
    {
        return m_xPropertySet;
    }
    std::vector<css::beans::PropertyValue>& getPendingProperties() { return m_aPendingProperties; }
    void setLineColorR(sal_uInt8 nLineColorR) { m_nLineColorR = nLineColorR; }
    sal_uInt8 getLineColorR() const { return m_nLineColorR; }
    void setLineColorG(sal_uInt8 nLineColorG) { m_nLineColorG = nLineColorG; }
    sal_uInt8 getLineColorG() const { return m_nLineColorG; }
    void setLineColorB(sal_uInt8 nLineColorB) { m_nLineColorB = nLineColorB; }
    sal_uInt8 getLineColorB() const { return m_nLineColorB; }
    void setHasLineColor(bool bHasLineColor) { m_bHasLineColor = bHasLineColor; }
    bool getHasLineColor() const { return m_bHasLineColor; }
    void setFillColorR(sal_uInt8 nFillColorR) { m_nFillColorR = nFillColorR; }
    sal_uInt8 getFillColorR() const { return m_nFillColorR; }
    void setFillColorG(sal_uInt8 nFillColorG) { m_nFillColorG = nFillColorG; }
    sal_uInt8 getFillColorG() const { return m_nFillColorG; }
    void setFillColorB(sal_uInt8 nFillColorB) { m_nFillColorB = nFillColorB; }
    sal_uInt8 getFillColorB() const { return m_nFillColorB; }
    void setHasFillColor(bool bHasFillColor) { m_bHasFillColor = bHasFillColor; }
    bool getHasFillColor() const { return m_bHasFillColor; }
    void setDhgt(sal_Int32 nDhgt) { m_nDhgt = nDhgt; }
    sal_Int32 getDhgt() const { return m_nDhgt; }
    void setFLine(sal_Int32 nFLine) { m_nFLine = nFLine; }
    sal_Int32 getFLine() const { return m_nFLine; }
    void setPolyLineCount(sal_Int32 nPolyLineCount) { m_nPolyLineCount = nPolyLineCount; }
    sal_Int32 getPolyLineCount() const { return m_nPolyLineCount; }
    std::vector<css::awt::Point>& getPolyLinePoints() { return m_aPolyLinePoints; }
    void setHadShapeText(bool bHadShapeText) { m_bHadShapeText = bHadShapeText; }
    bool getHadShapeText() const { return m_bHadShapeText; }

private:
    css::uno::Reference<css::drawing::XShape> m_xShape;
    css::uno::Reference<css::beans::XPropertySet> m_xPropertySet;
    std::vector<css::beans::PropertyValue> m_aPendingProperties;
    sal_uInt8 m_nLineColorR = 0;
    sal_uInt8 m_nLineColorG = 0;
    sal_uInt8 m_nLineColorB = 0;
    bool m_bHasLineColor = false;
    sal_uInt8 m_nFillColorR = 0;
    sal_uInt8 m_nFillColorG = 0;
    sal_uInt8 m_nFillColorB = 0;
    bool m_bHasFillColor = false;
    sal_Int32 m_nDhgt = 0;
    sal_Int32 m_nFLine = -1;
    sal_Int32 m_nPolyLineCount = 0;
    std::vector<css::awt::Point> m_aPolyLinePoints;
    bool m_bHadShapeText = false;
};

/// Stores the properties of a picture.
class RTFPicture : public virtual SvRefBase
{
public:
    sal_Int32 nWidth = 0;
    sal_Int32 nHeight = 0;
    sal_Int32 nGoalWidth = 0;
    sal_Int32 nGoalHeight = 0;
    sal_uInt16 nScaleX = 100;
    sal_uInt16 nScaleY = 100;
    short nCropT = 0;
    short nCropB = 0;
    short nCropL = 0;
    short nCropR = 0;
    sal_uInt16 eWMetafile = 0;
    RTFBmpStyle eStyle = RTFBmpStyle::NONE;
};

/// Stores the properties of a frame
class RTFFrame
{
private:
    RTFDocumentImpl* m_pDocumentImpl;
    sal_Int32 m_nX, m_nY, m_nW, m_nH;
    sal_Int32 m_nHoriPadding, m_nVertPadding;
    sal_Int32 m_nHoriAlign, m_nHoriAnchor, m_nVertAlign, m_nVertAnchor;
    Id m_nHRule;
    std::optional<Id> m_oWrap;

public:
    explicit RTFFrame(RTFParserState* pParserState);

    /// Convert the stored properties to Sprms
    RTFSprms getSprms();
    /// Store a property
    void setSprm(Id nId, Id nValue);
    /// If we got tokens indicating we're in a frame.
    bool hasProperties() const;

    bool operator==(const RTFFrame&) const = default;
};

/// State of the parser, which gets saved / restored when changing groups.
class RTFParserState
{
public:
    /// Maps to OOXML's ascii, cs or eastAsia.
    enum class RunType
    {
        NONE,
        LOCH,
        HICH,
        DBCH,
        LTRCH_RTLCH_1,
        LTRCH_RTLCH_2,
        RTLCH_LTRCH_1,
        RTLCH_LTRCH_2
    };

    explicit RTFParserState(RTFDocumentImpl* pDocumentImpl);

    void appendDestinationText(std::u16string_view rString)
    {
        if (m_pCurrentDestinationText)
            m_pCurrentDestinationText->append(rString);
    }

    void setPropName(const OUString& rPropName) { m_aPropName = rPropName; }
    OUString const& getPropName() const { return m_aPropName; }
    void setPropType(const css::uno::Type& rPropType) { m_aPropType = rPropType; }
    css::uno::Type const& getPropType() const { return m_aPropType; }
    void setTableRowWidthAfter(int nTableRowWidthAfter)
    {
        m_nTableRowWidthAfter = nTableRowWidthAfter;
    }
    int getTableRowWidthAfter() const { return m_nTableRowWidthAfter; }
    void setStartedTrackchange(bool bStartedTrackchange)
    {
        m_bStartedTrackchange = bStartedTrackchange;
    }
    bool getStartedTrackchange() const { return m_bStartedTrackchange; }
    void setCreatedShapeGroup(bool bCreatedShapeGroup)
    {
        m_bCreatedShapeGroup = bCreatedShapeGroup;
    }
    bool getCreatedShapeGroup() const { return m_bCreatedShapeGroup; }
    void setInShape(bool bInShape) { m_bInShape = bInShape; }
    bool getInShape() const { return m_bInShape; }
    void setInShapeGroup(bool bInShapeGroup) { m_bInShapeGroup = bInShapeGroup; }
    bool getInShapeGroup() const { return m_bInShapeGroup; }
    void setHadShapeText(bool bHadShapeText) { m_bHadShapeText = bHadShapeText; }
    bool getHadShapeText() const { return m_bHadShapeText; }
    void setInBackground(bool bInBackground) { m_bInBackground = bInBackground; }
    bool getInBackground() const { return m_bInBackground; }
    void setInListpicture(bool bInListpicture) { m_bInListpicture = bInListpicture; }
    bool getInListpicture() const { return m_bInListpicture; }
    void setCurrentBuffer(RTFBuffer_t* pCurrentBuffer) { m_pCurrentBuffer = pCurrentBuffer; }
    RTFBuffer_t* getCurrentBuffer() const { return m_pCurrentBuffer; }
    void setCurrentListOverrideIndex(int nCurrentListOverrideIndex)
    {
        m_nCurrentListOverrideIndex = nCurrentListOverrideIndex;
    }
    int getCurrentListOverrideIndex() const { return m_nCurrentListOverrideIndex; }
    void setCurrentListIndex(int nCurrentListIndex) { m_nCurrentListIndex = nCurrentListIndex; }
    int getCurrentListIndex() const { return m_nCurrentListIndex; }
    void setCurrentCharacterStyleIndex(int nCurrentCharacterStyleIndex)
    {
        m_nCurrentCharacterStyleIndex = nCurrentCharacterStyleIndex;
    }
    int getCurrentCharacterStyleIndex() const { return m_nCurrentCharacterStyleIndex; }
    void setCurrentStyleIndex(int nCurrentStyleIndex) { m_nCurrentStyleIndex = nCurrentStyleIndex; }
    int getCurrentStyleIndex() const { return m_nCurrentStyleIndex; }
    void setCurrentDestinationText(OUStringBuffer* pDestinationText)
    {
        m_pCurrentDestinationText = pDestinationText;
    }
    OUStringBuffer* getCurrentDestinationText() const { return m_pCurrentDestinationText; }
    OUStringBuffer& getDestinationText() { return m_aDestinationText; }
    void setMinute(sal_uInt16 nMinute) { m_nMinute = nMinute; }
    sal_uInt16 getMinute() const { return m_nMinute; }
    void setHour(sal_uInt16 nHour) { m_nHour = nHour; }
    sal_uInt16 getHour() const { return m_nHour; }
    void setDay(sal_uInt16 nDay) { m_nDay = nDay; }
    sal_uInt16 getDay() const { return m_nDay; }
    void setMonth(sal_uInt16 nMonth) { m_nMonth = nMonth; }
    sal_uInt16 getMonth() const { return m_nMonth; }
    void setYear(sal_uInt16 nYear) { m_nYear = nYear; }
    sal_uInt16 getYear() const { return m_nYear; }
    void setRunType(RunType eRunType) { m_eRunType = eRunType; }
    RunType getRunType() const { return m_eRunType; }
    RTFFrame& getFrame() { return m_aFrame; }
    RTFDrawingObject& getDrawingObject() { return m_aDrawingObject; }
    RTFShape& getShape() { return m_aShape; }
    RTFPicture& getPicture() { return m_aPicture; }
    void setLevelNumbersValid(bool bLevelNumbersValid)
    {
        m_bLevelNumbersValid = bLevelNumbersValid;
    }
    bool getLevelNumbersValid() const { return m_bLevelNumbersValid; }
    std::vector<sal_Int32>& getLevelNumbers() { return m_aLevelNumbers; }
    RTFSprms& getListLevelEntries() { return m_aListLevelEntries; }
    int& getListLevelNum() { return m_nListLevelNum; }
    void setBinaryToRead(int nBinaryToRead) { m_nBinaryToRead = nBinaryToRead; }
    int getBinaryToRead() const { return m_nBinaryToRead; }
    int& getCharsToSkip() { return m_nCharsToSkip; }
    void setUc(int nUc) { m_nUc = nUc; }
    int getUc() const { return m_nUc; }
    void setCurrentEncoding(rtl_TextEncoding nCurrentEncoding)
    {
        m_nCurrentEncoding = nCurrentEncoding;
    }
    rtl_TextEncoding getCurrentEncoding() const { return m_nCurrentEncoding; }
    RTFColorTableEntry& getCurrentColor() { return m_aCurrentColor; }
    RTFSprms& getTabAttributes() { return m_aTabAttributes; }
    RTFSprms& getTableCellAttributes() { return m_aTableCellAttributes; }
    RTFSprms& getTableCellSprms() { return m_aTableCellSprms; }
    RTFSprms& getTableRowAttributes() { return m_aTableRowAttributes; }
    RTFSprms& getTableRowSprms() { return m_aTableRowSprms; }
    RTFSprms& getSectionAttributes() { return m_aSectionAttributes; }
    RTFSprms& getSectionSprms() { return m_aSectionSprms; }
    RTFSprms& getParagraphAttributes() { return m_aParagraphAttributes; }
    RTFSprms& getParagraphSprms() { return m_aParagraphSprms; }
    RTFSprms& getCharacterAttributes() { return m_aCharacterAttributes; }
    RTFSprms& getCharacterSprms() { return m_aCharacterSprms; }
    RTFSprms& getTableAttributes() { return m_aTableAttributes; }
    RTFSprms& getTableSprms() { return m_aTableSprms; }
    void setBorderState(RTFBorderState nBorderState) { m_nBorderState = nBorderState; }
    RTFBorderState getBorderState() const { return m_nBorderState; }
    void setFieldStatus(RTFFieldStatus eFieldStatus) { m_eFieldStatus = eFieldStatus; }
    RTFFieldStatus getFieldStatus() const { return m_eFieldStatus; }
    void setFieldLocked(bool bSet) { m_bFieldLocked = bSet; }
    bool isFieldLocked() const { return m_bFieldLocked; }
    void setDestination(Destination eDestination) { m_eDestination = eDestination; }
    Destination getDestination() const { return m_eDestination; }
    void setInternalState(RTFInternalState nInternalState) { m_nInternalState = nInternalState; }
    RTFInternalState getInternalState() const { return m_nInternalState; }
    RTFDocumentImpl* getDocumentImpl() { return m_pDocumentImpl; }
    const OUString& getDocVar() { return m_aDocVar; }
    void appendDocVar(std::u16string_view aDocVar) { m_aDocVar += aDocVar; };
    const OUString& getDocVarName() { return m_aDocVarName; }
    void setDocVarName(const OUString& aDocVarName) { m_aDocVarName = aDocVarName; }
    void clearDocVarName() { m_aDocVarName = ""; }

private:
    RTFDocumentImpl* m_pDocumentImpl;
    RTFInternalState m_nInternalState;
    Destination m_eDestination;
    RTFFieldStatus m_eFieldStatus;
    bool m_bFieldLocked;
    RTFBorderState m_nBorderState;
    // font table, stylesheet table
    RTFSprms m_aTableSprms;
    RTFSprms m_aTableAttributes;
    // reset by plain
    RTFSprms m_aCharacterSprms;
    RTFSprms m_aCharacterAttributes;
    // reset by pard
    RTFSprms m_aParagraphSprms;
    RTFSprms m_aParagraphAttributes;
    // reset by sectd
    RTFSprms m_aSectionSprms;
    RTFSprms m_aSectionAttributes;
    // reset by trowd
    RTFSprms m_aTableRowSprms;
    RTFSprms m_aTableRowAttributes;
    // reset by cellx
    RTFSprms m_aTableCellSprms;
    RTFSprms m_aTableCellAttributes;
    // reset by tx
    RTFSprms m_aTabAttributes;

    RTFColorTableEntry m_aCurrentColor;

    rtl_TextEncoding m_nCurrentEncoding;

    /// Current \uc value.
    int m_nUc;
    /// Characters to skip, set to nUc by \u.
    int m_nCharsToSkip;
    /// Characters to read, once in binary mode.
    int m_nBinaryToRead;

    /// Next list level index to use when parsing list table.
    int m_nListLevelNum;
    /// List level entries, which will form a list entry later.
    RTFSprms m_aListLevelEntries;
    /// List of character positions in leveltext to replace.
    std::vector<sal_Int32> m_aLevelNumbers;
    /// If aLevelNumbers should be read at all.
    bool m_bLevelNumbersValid;

    RTFPicture m_aPicture;
    RTFShape m_aShape;
    RTFDrawingObject m_aDrawingObject;
    RTFFrame m_aFrame;

    RunType m_eRunType;

    // Info group.
    sal_Int16 m_nYear;
    sal_uInt16 m_nMonth;
    sal_uInt16 m_nDay;
    sal_uInt16 m_nHour;
    sal_uInt16 m_nMinute;

    /// Text from special destinations.
    OUStringBuffer m_aDestinationText{ 512 };
    /// point to the buffer of the current destination
    OUStringBuffer* m_pCurrentDestinationText;

    /// Index of the current style.
    int m_nCurrentStyleIndex;
    /// Index of the current character style.
    int m_nCurrentCharacterStyleIndex;
    /// Current listid, points to a listtable entry.
    int m_nCurrentListIndex = -1;
    /// Current ls, points to a listoverridetable entry.
    int m_nCurrentListOverrideIndex = -1;

    /// Points to the active buffer, if there is one.
    RTFBuffer_t* m_pCurrentBuffer;

    /// If we're inside a \listpicture group.
    bool m_bInListpicture;

    /// If we're inside a \background group.
    bool m_bInBackground;

    bool m_bHadShapeText;
    bool m_bInShapeGroup; ///< If we're inside a \shpgrp group.
    bool m_bInShape; ///< If we're inside a \shp group.
    bool m_bCreatedShapeGroup; ///< A GroupShape was created and pushed to the parent stack.
    bool m_bStartedTrackchange; ///< Track change is started, need to end it before popping.

    /// User-defined property: key name.
    OUString m_aPropName;
    /// User-defined property: value type.
    css::uno::Type m_aPropType;

    /// Width of invisible cell at the end of the row.
    int m_nTableRowWidthAfter;

    /// For importing document variables which are not referenced in the document
    OUString m_aDocVar;
    OUString m_aDocVarName;
};

/// An RTF stack is similar to std::stack, except that it has an operator[].
struct RTFStack
{
private:
    std::deque<RTFParserState> m_Impl;

public:
    RTFParserState& top()
    {
        if (m_Impl.empty())
            throw css::io::WrongFormatException(
                u"Parser state is empty! Invalid usage of destination braces in RTF?"_ustr,
                nullptr);
        return m_Impl.back();
    }
    void pop()
    {
        if (m_Impl.empty())
            throw css::io::WrongFormatException(
                u"Parser state is empty! Invalid usage of destination braces in RTF?"_ustr,
                nullptr);
        return m_Impl.pop_back();
    }
    void push(RTFParserState const& rState) { return m_Impl.push_back(rState); }
    bool empty() const { return m_Impl.empty(); }
    size_t size() const { return m_Impl.size(); }
    const RTFParserState& operator[](size_t nIndex) const { return m_Impl[nIndex]; }
    RTFParserState& operator[](size_t nIndex) { return m_Impl[nIndex]; }
};

void putBorderProperty(RTFStack& aStates, Id nId, const RTFValue::Pointer_t& pValue);
void putNestedSprm(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue,
                   RTFConflictPolicy ePolicy = RTFConflictPolicy::Append);
Id getParagraphBorder(sal_uInt32 nIndex);
void putNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue,
                        RTFConflictPolicy ePolicy = RTFConflictPolicy::Overwrite,
                        bool bAttribute = true);
bool eraseNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId);

/// Looks up the nParent then the nested nId attribute in rSprms.
RTFValue::Pointer_t getNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId);

/// Looks up the nParent then the nested nId sprm in rSprms.
RTFValue::Pointer_t getNestedSprm(RTFSprms& rSprms, Id nParent, Id nId);

/// Checks if rName is contained at least once in rProperties as a key.
bool findPropertyName(const std::vector<css::beans::PropertyValue>& rProperties,
                      const OUString& rName);
RTFSprms& getLastAttributes(RTFSprms& rSprms, Id nId);
OUString DTTM22OUString(tools::Long nDTTM);

/// Implementation of the RTFDocument interface.
class RTFDocumentImpl : public RTFDocument, public RTFListener
{
public:
    using Pointer_t = tools::SvRef<RTFDocumentImpl>;
    RTFDocumentImpl(css::uno::Reference<css::uno::XComponentContext> const& xContext,
                    css::uno::Reference<css::io::XInputStream> const& xInputStream,
                    rtl::Reference<SwXTextDocument> const& xDstDoc,
                    css::uno::Reference<css::frame::XFrame> const& xFrame,
                    css::uno::Reference<css::task::XStatusIndicator> const& xStatusIndicator,
                    const utl::MediaDescriptor& rMediaDescriptor);
    ~RTFDocumentImpl() override;

    // RTFDocument
    void resolve(Stream& rMapper) override;

    // RTFListener
    RTFError dispatchDestination(RTFKeyword nKeyword) override;
    RTFError dispatchFlag(RTFKeyword nKeyword) override;
    /// Dispatches flags related to Positioned Wrapped Tables.
    bool dispatchFloatingTableFlag(RTFKeyword nKeyword);
    RTFError dispatchSymbol(RTFKeyword nKeyword) override;
    RTFError dispatchToggle(RTFKeyword nKeyword, bool bParam, int nParam) override;
    RTFError dispatchValue(RTFKeyword nKeyword, int nParam) override;
    bool dispatchTableSprmValue(RTFKeyword nKeyword, int nParam);
    bool dispatchCharacterSprmValue(RTFKeyword nKeyword, int nParam);
    bool dispatchCharacterAttributeValue(RTFKeyword nKeyword, int nParam);
    bool dispatchParagraphSprmValue(RTFKeyword nKeyword, int nParam);
    bool dispatchInfoValue(RTFKeyword nKeyword, int nParam);
    bool dispatchFrameValue(RTFKeyword nKeyword, int nParam);
    bool dispatchTableValue(RTFKeyword nKeyword, int nParam);
    RTFError resolveChars(char ch) override;
    RTFError pushState() override;
    RTFError beforePopState(RTFParserState& rState);
    RTFError popState() override;
    void afterPopState(RTFParserState& rState);
    Destination getDestination() override;
    void setDestination(Destination eDestination) override;
    RTFInternalState getInternalState() override;
    void setInternalState(RTFInternalState nInternalState) override;
    bool getSkipUnknown() override;
    void setSkipUnknown(bool bSkipUnknown) override;
    void finishSubstream() override;
    bool isSubstream() const override;

    Stream& Mapper() { return *m_pMapperStream; }
    void setSuperstream(RTFDocumentImpl* pSuperstream);
    const rtl::Reference<SwXTextDocument>& getTextDocument() const { return m_xDstDoc; }
    bool isInBackground();
    void setDestinationText(std::u16string_view rString);
    /// Resolve a picture: If not inline, then anchored.
    void resolvePict(bool bInline, css::uno::Reference<css::drawing::XShape> const& rShape);

    /// If this is the first run of the document, starts the initial paragraph.
    void checkFirstRun();
    /// Send NS_ooxml::LN_settings_settings to dmapper.
    void outputSettingsTable();
    /// If the initial paragraph is started.
    bool getFirstRun() const { return m_bFirstRun; }
    /// If we need to add a dummy paragraph before a section break.
    void setNeedPar(bool bNeedPar);
    /// Return the dmapper index of an RTF index for fonts.
    int getFontIndex(int nIndex);
    /// Return the name of the font, based on a dmapper index.
    OUString getFontName(int nIndex);
    /// Return the style name of an RTF style index.
    OUString getStyleName(int nIndex);
    /// Return the style type of an RTF style index.
    Id getStyleType(int nIndex);
    /// Return the encoding associated with a font index.
    rtl_TextEncoding getEncoding(int nFontIndex);
    /// Get the default parser state.
    RTFParserState& getDefaultState();
    oox::GraphicHelper& getGraphicHelper();
    /// Are we inside the stylesheet table?
    bool isStyleSheetImport();
    /// Resets m_aStates.top().aFrame.
    void resetFrame();
    /// Buffers properties to be sent later.
    void bufferProperties(RTFBuffer_t& rBuffer, const RTFValue::Pointer_t& pValue,
                          const tools::SvRef<TableRowBuffer>& pTableProperties, Id nStyleType = 0);
    /// implement non-obvious RTF specific style inheritance
    RTFReferenceTable::Entries_t deduplicateStyleTable();

private:
    SvStream& Strm();
    Color getColorTable(sal_uInt32 nIndex);
    writerfilter::Reference<Properties>::Pointer_t createStyleProperties();
    void resetSprms();
    void resetAttributes();
    void resolveSubstream(std::size_t nPos, Id nId);
    void resolveSubstream(std::size_t nPos, Id nId, OUString const& rIgnoreFirst);

    void text(OUString& rString);
    // Sends a single character to dmapper, taking care of buffering.
    void singleChar(sal_uInt8 nValue, bool bRunProps = false);
    // Sends run properties to dmapper, taking care of buffering.
    void runProps();
    void runBreak();
    void parBreak();
    void tableBreak();
    writerfilter::Reference<Properties>::Pointer_t getProperties(const RTFSprms& rAttributes,
                                                                 RTFSprms const& rSprms,
                                                                 Id nStyleType,
                                                                 bool bReplay = false);
    void checkNeedPap();
    void handleFontTableEntry();
    void sectBreak(bool bFinal = false);
    static void set_tblInd(RTFSprms& tableRowSprms, int val);
    void prepareProperties(RTFParserState& rState,
                           writerfilter::Reference<Properties>::Pointer_t& o_rpParagraphProperties,
                           writerfilter::Reference<Properties>::Pointer_t& o_rpFrameProperties,
                           writerfilter::Reference<Properties>::Pointer_t& o_rpTableRowProperties,
                           int nCells, int nCurrentCellX, int nTRLeft);
    /// Send the passed properties to dmapper.
    void sendProperties(writerfilter::Reference<Properties>::Pointer_t const& pParagraphProperties,
                        writerfilter::Reference<Properties>::Pointer_t const& pFrameProperties,
                        writerfilter::Reference<Properties>::Pointer_t const& pTableRowProperties);
    void replayRowBuffer(RTFBuffer_t& rBuffer, ::std::deque<RTFSprms>& rCellsSprms,
                         ::std::deque<RTFSprms>& rCellsAttributes, int nCells);
    void replayBuffer(RTFBuffer_t& rBuffer, RTFSprms* pSprms, RTFSprms const* pAttributes);
    /// If we have some unicode or hex characters to send.
    void checkUnicode(bool bUnicode, bool bHex);
    /// If we need a final section break at the end of the document.
    void setNeedSect(bool bNeedSect);
    void resetTableRowProperties();
    void backupTableRowProperties();
    void restoreTableRowProperties();
    /// Turns the destination text into an input stream of the current OLE attributes.
    RTFError handleEmbeddedObject();

    css::uno::Reference<css::uno::XComponentContext> const& m_xContext;
    css::uno::Reference<css::io::XInputStream> const& m_xInputStream;
    rtl::Reference<SwXTextDocument> const& m_xDstDoc;
    css::uno::Reference<css::frame::XFrame> const& m_xFrame;
    css::uno::Reference<css::task::XStatusIndicator> const& m_xStatusIndicator;
    css::uno::Reference<css::document::XDocumentProperties> m_xDocumentProperties;
    std::unique_ptr<SvStream> m_pInStream;
    Stream* m_pMapperStream;
    tools::SvRef<RTFSdrImport> m_pSdrImport;
    tools::SvRef<RTFTokenizer> m_pTokenizer;
    RTFStack m_aStates;
    /// Read by RTF_PARD.
    RTFParserState m_aDefaultState;
    bool m_bSkipUnknown;
    /// Font index <-> encoding map, *not* part of the parser state
    std::map<int, rtl_TextEncoding> m_aFontEncodings;
    /// Font index <-> name map.
    std::map<int, OUString> m_aFontNames;
    /// Maps the non-continuous font indexes to the continuous dmapper indexes.
    std::vector<int> m_aFontIndexes;
    /// Maps style indexes to style names.
    std::map<int, OUString> m_aStyleNames;
    /// Maps style indexes to style types.
    std::map<int, Id> m_aStyleTypes;
    /// Color index <-> RGB color value map
    std::vector<Color> m_aColorTable;
    /// to start initial paragraph / section after font/style tables
    bool m_bFirstRun;
    /// except in the case of tables in initial multicolumn section (global for assertion)
    bool m_bFirstRunException;
    /// If paragraph properties should be emitted on next run.
    bool m_bNeedPap;
    /// If we need to emit a CR at the end of substream.
    bool m_bNeedCr;
    /// Original value of m_bNeedCr -- saved/restored before/after textframes.
    bool m_bNeedCrOrig;
    bool m_bNeedPar;
    /// If set, an empty paragraph will be added at the end of the document.
    bool m_bNeedFinalPar;
    /// a synthetic \par was dispatched at the end of the current section
    bool m_bParAtEndOfSection = false;
    /// The list table and list override table combined.
    RTFSprms m_aListTableSprms;
    /// Maps between listoverridetable and listtable indexes.
    std::map<int, int> m_aListOverrideTable;
    /// Maps listtable indexes to listtable entries.
    std::map<int, RTFValue::Pointer_t> m_aListTable;
    /// Index of the current list level in a list table entry.
    int m_nListLevel = -1;
    /// Maps List level indexes to removed values in the current list entry.
    std::map<int, int> m_aInvalidListLevelFirstIndents;
    /// Maps list table indexes to levels (and their removed values).
    std::map<int, std::map<int, int>> m_aInvalidListTableFirstIndents;
    /// The settings table attributes.
    RTFSprms m_aSettingsTableAttributes;
    /// The settings table sprms.
    RTFSprms m_aSettingsTableSprms;

    std::shared_ptr<oox::GraphicHelper> m_pGraphicHelper;

    /// cell props buffer for nested tables, reset by \nestrow
    /// the \nesttableprops is a destination and must follow the
    /// nested cells, so it should be sufficient to store the
    /// currently active one, no need for a stack of them
    int m_nNestedCells;
    std::deque<RTFSprms> m_aNestedTableCellsSprms;
    std::deque<RTFSprms> m_aNestedTableCellsAttributes;
    /// cell props buffer for top-level table, reset by \row
    int m_nTopLevelCells;
    std::deque<RTFSprms> m_aTopLevelTableCellsSprms;
    std::deque<RTFSprms> m_aTopLevelTableCellsAttributes;
    /// backup of top-level props, to support inheriting cell props
    int m_nInheritingCells;
    std::deque<RTFSprms> m_aTableInheritingCellsSprms;
    std::deque<RTFSprms> m_aTableInheritingCellsAttributes;

    // Left row margin (for nested and top-level rows)
    int m_nNestedTRLeft;
    int m_nTopLevelTRLeft;

    /// Current cellx value (nested table)
    int m_nNestedCurrentCellX;
    /// Current cellx value (top-level table)
    int m_nTopLevelCurrentCellX;

    // Backup of what \trowd clears, to work around invalid input.
    RTFSprms m_aBackupTableRowSprms;
    RTFSprms m_aBackupTableRowAttributes;
    int m_nBackupTopLevelCurrentCellX;

    /// Buffered table cells, till cell definitions are not reached.
    /// for nested table, one buffer per table level
    std::deque<RTFBuffer_t> m_aTableBufferStack;
    /// Buffered superscript, till footnote is reached (or not).
    RTFBuffer_t m_aSuperBuffer;

    /// Superstream of this substream.
    RTFDocumentImpl* m_pSuperstream;
    /// Type of the stream: header, footer, footnote, etc.
    Id m_nStreamType;
    std::queue<std::pair<Id, std::size_t>> m_nHeaderFooterPositions;
    std::size_t m_nGroupStartPos;
    /// Ignore the first occurrence of this text.
    OUString m_aIgnoreFirst;
    /// Bookmark name <-> index map.
    std::map<OUString, int> m_aBookmarks;
    /// Revision index <-> author map.
    std::map<int, OUString> m_aAuthors;
    /// Annotation author of the next annotation.
    OUString m_aAuthor;
    /// Initials of author of the next annotation.
    OUString m_aAuthorInitials;

    RTFSprms m_aFormfieldSprms;
    RTFSprms m_aFormfieldAttributes;
    RTFFormFieldType m_nFormFieldType;

    /// OLE attributes are attributes of the ooxml:OLEObject_OLEObject sprm.
    RTFSprms m_aOLEAttributes;
    RTFSprms m_aObjectAttributes;
    /** If we are in an object group and if the we use its
     *  \objdata element.
     *  (if we don't use the \objdata we use the \result element)*/
    bool m_bObject;
    /// If the data for a picture is a binary one, it's stored here.
    std::shared_ptr<SvStream> m_pBinaryData;

    RTFReferenceTable::Entries_t m_aFontTableEntries;
    int m_nCurrentFontIndex;
    /// Used only during font table parsing till we don't know the font name.
    int m_nCurrentEncoding;
    /// Raw default font index, use getFont() on it to get a real one.
    int m_nDefaultFontIndex;

    /// To avoid copying entries between DomainMapper instances it is stored as pointer
    std::shared_ptr<RTFReferenceTable::Entries_t> m_pStyleTableEntries;
    int m_nCurrentStyleIndex;
    bool m_bFormField;
    /// For the INCLUDEPICTURE field's argument.
    OUString m_aPicturePath;
    // Unicode characters are collected here so we don't have to send them one by one.
    OUStringBuffer m_aUnicodeBuffer{ 512 };
    /// Same for hex characters.
    OStringBuffer m_aHexBuffer{ 512 };
    /// Formula import.
    oox::formulaimport::XmlStreamBuilder m_aMathBuffer;
    /// Normal text property, that is math italic and math spacing are not applied to the current run.
    bool m_bMathNor;
    /// If the next continuous section break should be ignored.
    bool m_bIgnoreNextContSectBreak;
    /// clean up a synthetic page break, see RTF_PAGE
    /// if inactive value is -1, otherwise the RTF_SKB* to restore
    RTFKeyword m_nResetBreakOnSectBreak;
    /// If a section break is needed before the end of the doc (false right after a section break).
    bool m_bNeedSect;
    /// If aFrame.hasProperties() was true in the previous state.
    bool m_bWasInFrame;
    /// A picture was seen in the current paragraph.
    bool m_bHadPicture;
    /// The document has multiple sections.
    bool m_bHadSect;
    /// Max width of the rows in the current table.
    int m_nCellxMax;
    /// ID of the next \listlevel picture.
    int m_nListPictureId;

    /// New document means not pasting into an existing one.
    bool m_bIsNewDoc;
    /// The media descriptor contains e.g. the base URL of the document.
    const utl::MediaDescriptor& m_rMediaDescriptor;

    /// Flags for ensuring that only one header and footer is added per section
    bool m_hasRHeader;
    bool m_hasFHeader;
    bool m_hasRFooter;
    bool m_hasFFooter;
};
} // namespace writerfilter::rtftok

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
