/* -*- 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/.
 */

#include <sal/config.h>

#include <string_view>

#include "rtfdocumentimpl.hxx"

#include <com/sun/star/document/DocumentProperties.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <com/sun/star/text/VertOrientation.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>

#include <filter/msfilter/escherex.hxx>
#include <rtl/character.hxx>
#include <tools/stream.hxx>
#include <sal/log.hxx>

#include <dmapper/DomainMapperFactory.hxx>
#include <ooxml/resourceids.hxx>

#include "rtflookahead.hxx"
#include "rtfreferenceproperties.hxx"
#include "rtfsdrimport.hxx"
#include "rtfskipdestination.hxx"
#include "rtftokenizer.hxx"
#include <unotxdoc.hxx>

using namespace com::sun::star;

namespace writerfilter::rtftok
{
RTFError RTFDocumentImpl::dispatchDestination(RTFKeyword nKeyword)
{
    setNeedSect(true);
    checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
    RTFSkipDestination aSkip(*this);
    // special case \upr: ignore everything except nested \ud
    if (Destination::UPR == m_aStates.top().getDestination() && RTFKeyword::UD != nKeyword)
    {
        m_aStates.top().setDestination(Destination::SKIP);
        aSkip.setParsed(false);
    }
    else
        switch (nKeyword)
        {
            case RTFKeyword::RTF:
                break;
            case RTFKeyword::FONTTBL:
                m_aStates.top().setDestination(Destination::FONTTABLE);
                break;
            case RTFKeyword::COLORTBL:
                m_aStates.top().setDestination(Destination::COLORTABLE);
                break;
            case RTFKeyword::STYLESHEET:
                m_aStates.top().setDestination(Destination::STYLESHEET);
                break;
            case RTFKeyword::FIELD:
                m_aStates.top().setDestination(Destination::FIELD);
                m_aStates.top().setFieldLocked(false);
                break;
            case RTFKeyword::DOCVAR:
                m_aStates.top().setDestination(Destination::DOCVAR);
                break;
            case RTFKeyword::FLDINST:
            {
                // Look for the field type
                sal_uInt64 const nPos = Strm().Tell();
                OStringBuffer aBuf;
                char ch = 0;
                bool bFoundCode = false;
                bool bInKeyword = false;
                while (!bFoundCode && ch != '}')
                {
                    Strm().ReadChar(ch);
                    if ('\\' == ch)
                        bInKeyword = true;
                    if (!bInKeyword && rtl::isAsciiAlphanumeric(static_cast<unsigned char>(ch)))
                        aBuf.append(ch);
                    else if (bInKeyword && rtl::isAsciiWhiteSpace(static_cast<unsigned char>(ch)))
                        bInKeyword = false;
                    if (!aBuf.isEmpty()
                        && !rtl::isAsciiAlphanumeric(static_cast<unsigned char>(ch)))
                        bFoundCode = true;
                }

                if (std::string_view(aBuf) == "INCLUDEPICTURE")
                {
                    // Extract the field argument of INCLUDEPICTURE: we handle that
                    // at a tokenizer level, as DOCX has no such field.
                    aBuf.append(ch);
                    while (true)
                    {
                        Strm().ReadChar(ch);
                        if (ch == '}')
                            break;
                        aBuf.append(ch);
                    }
                    OUString aFieldCommand = OStringToOUString(aBuf, RTL_TEXTENCODING_UTF8);
                    std::tuple<OUString, std::vector<OUString>, std::vector<OUString>> aResult
                        = writerfilter::dmapper::splitFieldCommand(aFieldCommand);
                    m_aPicturePath
                        = std::get<1>(aResult).empty() ? OUString() : std::get<1>(aResult).front();
                }

                Strm().Seek(nPos);

                // Form data should be handled only for form fields if any
                if (aBuf.toString().indexOf("FORM") != -1)
                    m_bFormField = true;

                singleChar(cFieldStart);
                m_aStates.top().setDestination(Destination::FIELDINSTRUCTION);
            }
            break;
            case RTFKeyword::FLDRSLT:
                m_aStates.top().setDestination(Destination::FIELDRESULT);
                break;
            case RTFKeyword::LISTTABLE:
                m_aStates.top().setDestination(Destination::LISTTABLE);
                break;
            case RTFKeyword::LISTPICTURE:
                m_aStates.top().setDestination(Destination::LISTPICTURE);
                m_aStates.top().setInListpicture(true);
                break;
            case RTFKeyword::LIST:
                m_aStates.top().setDestination(Destination::LISTENTRY);
                break;
            case RTFKeyword::LISTNAME:
                m_aStates.top().setDestination(Destination::LISTNAME);
                break;
            case RTFKeyword::LFOLEVEL:
                m_aStates.top().setDestination(Destination::LFOLEVEL);
                m_aStates.top().getTableSprms().clear();
                break;
            case RTFKeyword::LISTOVERRIDETABLE:
                m_aStates.top().setDestination(Destination::LISTOVERRIDETABLE);
                break;
            case RTFKeyword::LISTOVERRIDE:
                m_aStates.top().setDestination(Destination::LISTOVERRIDEENTRY);
                break;
            case RTFKeyword::LISTLEVEL:
                m_aStates.top().setDestination(Destination::LISTLEVEL);
                ++m_nListLevel;
                break;
            case RTFKeyword::LEVELTEXT:
                m_aStates.top().setDestination(Destination::LEVELTEXT);
                break;
            case RTFKeyword::LEVELNUMBERS:
                m_aStates.top().setDestination(Destination::LEVELNUMBERS);
                break;
            case RTFKeyword::SHPPICT:
                resetFrame();
                m_aStates.top().setDestination(Destination::SHPPICT);
                break;
            case RTFKeyword::PICT:
                if (m_aStates.top().getDestination() != Destination::SHAPEPROPERTYVALUE)
                    m_aStates.top().setDestination(Destination::PICT); // as character
                else
                    m_aStates.top().setDestination(
                        Destination::SHAPEPROPERTYVALUEPICT); // anchored inside a shape
                break;
            case RTFKeyword::PICPROP:
                m_aStates.top().setDestination(Destination::PICPROP);
                break;
            case RTFKeyword::SP:
                m_aStates.top().setDestination(Destination::SHAPEPROPERTY);
                break;
            case RTFKeyword::SN:
                m_aStates.top().setDestination(Destination::SHAPEPROPERTYNAME);
                break;
            case RTFKeyword::SV:
                m_aStates.top().setDestination(Destination::SHAPEPROPERTYVALUE);
                break;
            case RTFKeyword::SHP:
                m_bNeedCrOrig = m_bNeedCr;
                m_aStates.top().setDestination(Destination::SHAPE);
                m_aStates.top().setInShape(true);
                break;
            case RTFKeyword::SHPINST:
                m_aStates.top().setDestination(Destination::SHAPEINSTRUCTION);
                break;
            case RTFKeyword::NESTTABLEPROPS:
                // do not set any properties of outer table at nested table!
                m_aStates.top().getTableCellSprms() = m_aDefaultState.getTableCellSprms();
                m_aStates.top().getTableCellAttributes() = m_aDefaultState.getTableCellAttributes();
                m_aNestedTableCellsSprms.clear();
                m_aNestedTableCellsAttributes.clear();
                m_nNestedCells = 0;
                m_aStates.top().setDestination(Destination::NESTEDTABLEPROPERTIES);
                break;
            case RTFKeyword::HEADER:
            case RTFKeyword::FOOTER:
            case RTFKeyword::HEADERL:
            case RTFKeyword::HEADERR:
            case RTFKeyword::HEADERF:
            case RTFKeyword::FOOTERL:
            case RTFKeyword::FOOTERR:
            case RTFKeyword::FOOTERF:
                if (!m_pSuperstream)
                {
                    Id nId = 0;
                    std::size_t nPos = m_nGroupStartPos - 1;
                    switch (nKeyword)
                    {
                        case RTFKeyword::HEADER:
                            if (!m_hasRHeader)
                            {
                                nId = NS_ooxml::LN_headerr;
                                m_hasRHeader = true;
                            }
                            break;
                        case RTFKeyword::FOOTER:
                            if (!m_hasRFooter)
                            {
                                nId = NS_ooxml::LN_footerr;
                                m_hasRFooter = true;
                            }
                            break;
                        case RTFKeyword::HEADERL:
                            nId = NS_ooxml::LN_headerl;
                            break;
                        case RTFKeyword::HEADERR:
                            nId = NS_ooxml::LN_headerr;
                            break;
                        case RTFKeyword::HEADERF:
                            if (!m_hasFHeader)
                            {
                                nId = NS_ooxml::LN_headerf;
                                m_hasFHeader = true;
                            }
                            break;
                        case RTFKeyword::FOOTERL:
                            nId = NS_ooxml::LN_footerl;
                            break;
                        case RTFKeyword::FOOTERR:
                            nId = NS_ooxml::LN_footerr;
                            break;
                        case RTFKeyword::FOOTERF:
                            if (!m_hasFFooter)
                            {
                                nId = NS_ooxml::LN_footerf;
                                m_hasFFooter = true;
                            }
                            break;
                        default:
                            break;
                    }

                    if (nId != 0)
                        m_nHeaderFooterPositions.push(std::make_pair(nId, nPos));

                    m_aStates.top().setDestination(Destination::SKIP);
                }
                break;
            case RTFKeyword::FOOTNOTE:
                checkFirstRun();
                if (!m_pSuperstream)
                {
                    Id nId = NS_ooxml::LN_footnote;

                    {
                        // Check if this is an endnote.
                        sal_uInt64 const nCurrent = Strm().Tell();
                        char buf[7];
                        std::string_view aKeyword(buf, Strm().ReadBytes(buf, std::size(buf)));
                        Strm().Seek(nCurrent);
                        if (aKeyword == "\\ftnalt")
                            nId = NS_ooxml::LN_endnote;
                    }

                    if (m_aStates.top().getCurrentBuffer() == &m_aSuperBuffer)
                        m_aStates.top().setCurrentBuffer(nullptr);
                    bool bCustomMark = false;
                    OUString aCustomMark;
                    for (auto const& elem : m_aSuperBuffer)
                    {
                        if (std::get<0>(elem) == RTFBufferTypes::UText)
                        {
                            aCustomMark = std::get<1>(elem)->getString();
                            bCustomMark = true;
                        }
                    }
                    m_aSuperBuffer.clear();
                    m_aStates.top().setDestination(Destination::FOOTNOTE);
                    Mapper().startCharacterGroup();
                    runProps();
                    if (!m_aStates.top().getCurrentBuffer())
                        resolveSubstream(m_nGroupStartPos - 1, nId, aCustomMark);
                    else
                    {
                        RTFSprms aAttributes;
                        aAttributes.set(Id(0), new RTFValue(m_nGroupStartPos - 1));
                        aAttributes.set(Id(1), new RTFValue(nId));
                        aAttributes.set(Id(2), new RTFValue(aCustomMark));
                        m_aStates.top().getCurrentBuffer()->push_back(Buf_t(
                            RTFBufferTypes::ResolveSubstream, new RTFValue(aAttributes), nullptr));
                    }
                    if (bCustomMark)
                    {
                        m_aStates.top().getCharacterAttributes().clear();
                        m_aStates.top().getCharacterSprms().clear();
                        auto pValue = new RTFValue(1);
                        m_aStates.top().getCharacterAttributes().set(
                            NS_ooxml::LN_CT_FtnEdnRef_customMarkFollows, pValue);
                        text(aCustomMark);
                    }
                    Mapper().endCharacterGroup();
                    m_aStates.top().setDestination(Destination::SKIP);
                }
                break;
            case RTFKeyword::BKMKSTART:
                m_aStates.top().setDestination(Destination::BOOKMARKSTART);
                break;
            case RTFKeyword::BKMKEND:
                m_aStates.top().setDestination(Destination::BOOKMARKEND);
                break;
            case RTFKeyword::XE:
                m_aStates.top().setDestination(Destination::INDEXENTRY);
                break;
            case RTFKeyword::TC:
            case RTFKeyword::TCN:
                m_aStates.top().setDestination(Destination::TOCENTRY);
                break;
            case RTFKeyword::REVTBL:
                m_aStates.top().setDestination(Destination::REVISIONTABLE);
                break;
            case RTFKeyword::ANNOTATION:
                if (!m_pSuperstream)
                {
                    if (!m_aStates.top().getCurrentBuffer())
                    {
                        resolveSubstream(m_nGroupStartPos - 1, NS_ooxml::LN_annotation);
                    }
                    else
                    {
                        RTFSprms aAttributes;
                        aAttributes.set(Id(0), new RTFValue(m_nGroupStartPos - 1));
                        aAttributes.set(Id(1), new RTFValue(NS_ooxml::LN_annotation));
                        aAttributes.set(Id(2), new RTFValue(OUString()));
                        m_aStates.top().getCurrentBuffer()->push_back(Buf_t(
                            RTFBufferTypes::ResolveSubstream, new RTFValue(aAttributes), nullptr));
                    }
                    m_aStates.top().setDestination(Destination::SKIP);
                }
                else
                {
                    // If there is an author set, emit it now.
                    if (!m_aAuthor.isEmpty() || !m_aAuthorInitials.isEmpty())
                    {
                        RTFSprms aAttributes;
                        if (!m_aAuthor.isEmpty())
                        {
                            auto pValue = new RTFValue(m_aAuthor);
                            aAttributes.set(NS_ooxml::LN_CT_TrackChange_author, pValue);
                        }
                        if (!m_aAuthorInitials.isEmpty())
                        {
                            auto pValue = new RTFValue(m_aAuthorInitials);
                            aAttributes.set(NS_ooxml::LN_CT_Comment_initials, pValue);
                        }
                        writerfilter::Reference<Properties>::Pointer_t pProperties
                            = new RTFReferenceProperties(std::move(aAttributes));
                        Mapper().props(pProperties);
                    }
                }
                break;
            case RTFKeyword::SHPTXT:
            case RTFKeyword::DPTXBXTEXT:
            {
                bool bPictureFrame = false;
                for (const auto& rProperty : m_aStates.top().getShape().getProperties())
                {
                    if (rProperty.first == "shapeType"
                        && rProperty.second
                               == std::u16string_view(
                                      OUString::number(ESCHER_ShpInst_PictureFrame)))
                    {
                        bPictureFrame = true;
                        break;
                    }
                }
                if (bPictureFrame)
                    // Skip text on picture frames.
                    m_aStates.top().setDestination(Destination::SKIP);
                else
                {
                    m_aStates.top().setDestination(Destination::SHAPETEXT);
                    checkFirstRun();
                    dispatchFlag(RTFKeyword::PARD);
                    m_bNeedPap = true;
                    if (nKeyword == RTFKeyword::SHPTXT)
                    {
                        if (!m_aStates.top().getCurrentBuffer())
                            m_pSdrImport->resolve(m_aStates.top().getShape(), false,
                                                  RTFSdrImport::SHAPE);
                        else
                        {
                            auto pValue = new RTFValue(m_aStates.top().getShape());
                            m_aStates.top().getCurrentBuffer()->push_back(
                                Buf_t(RTFBufferTypes::StartShape, pValue, nullptr));
                        }
                    }
                }
            }
            break;
            case RTFKeyword::FORMFIELD:
                if (m_aStates.top().getDestination() == Destination::FIELDINSTRUCTION)
                    m_aStates.top().setDestination(Destination::FORMFIELD);
                break;
            case RTFKeyword::FFNAME:
                m_aStates.top().setDestination(Destination::FORMFIELDNAME);
                break;
            case RTFKeyword::FFL:
                m_aStates.top().setDestination(Destination::FORMFIELDLIST);
                break;
            case RTFKeyword::DATAFIELD:
                m_aStates.top().setDestination(Destination::DATAFIELD);
                break;
            case RTFKeyword::INFO:
                m_aStates.top().setDestination(Destination::INFO);
                break;
            case RTFKeyword::CREATIM:
                m_aStates.top().setDestination(Destination::CREATIONTIME);
                break;
            case RTFKeyword::REVTIM:
                m_aStates.top().setDestination(Destination::REVISIONTIME);
                break;
            case RTFKeyword::PRINTIM:
                m_aStates.top().setDestination(Destination::PRINTTIME);
                break;
            case RTFKeyword::AUTHOR:
                m_aStates.top().setDestination(Destination::AUTHOR);
                break;
            case RTFKeyword::KEYWORDS:
                m_aStates.top().setDestination(Destination::KEYWORDS);
                break;
            case RTFKeyword::OPERATOR:
                m_aStates.top().setDestination(Destination::OPERATOR);
                break;
            case RTFKeyword::COMPANY:
                m_aStates.top().setDestination(Destination::COMPANY);
                break;
            case RTFKeyword::COMMENT:
                m_aStates.top().setDestination(Destination::COMMENT);
                break;
            case RTFKeyword::OBJECT:
            {
                // beginning of an OLE Object
                m_aStates.top().setDestination(Destination::OBJECT);

                // check if the object is in a special container (e.g. a table)
                if (!m_aStates.top().getCurrentBuffer())
                {
                    // the object is in a table or another container.
                    // Don't try to treat it as an OLE object (fdo#53594).
                    // Use the \result (RTFKeyword::RESULT) element of the object instead,
                    // the result element contain picture representing the OLE Object.
                    m_bObject = true;
                }
            }
            break;
            case RTFKeyword::OBJDATA:
                // check if the object is in a special container (e.g. a table)
                if (m_aStates.top().getCurrentBuffer())
                {
                    // the object is in a table or another container.
                    // Use the \result (RTFKeyword::RESULT) element of the object instead,
                    // of the \objdata.
                    m_aStates.top().setDestination(Destination::SKIP);
                }
                else
                {
                    m_aStates.top().setDestination(Destination::OBJDATA);
                }
                break;
            case RTFKeyword::OBJCLASS:
                m_aStates.top().setDestination(Destination::OBJCLASS);
                break;
            case RTFKeyword::RESULT:
                m_aStates.top().setDestination(Destination::RESULT);
                break;
            case RTFKeyword::ATNDATE:
                m_aStates.top().setDestination(Destination::ANNOTATIONDATE);
                break;
            case RTFKeyword::ATNAUTHOR:
                m_aStates.top().setDestination(Destination::ANNOTATIONAUTHOR);
                break;
            case RTFKeyword::ATNREF:
                m_aStates.top().setDestination(Destination::ANNOTATIONREFERENCE);
                break;
            case RTFKeyword::FALT:
                m_aStates.top().setDestination(Destination::FALT);
                break;
            case RTFKeyword::FLYMAINCNT:
                m_aStates.top().setDestination(Destination::FLYMAINCONTENT);
                break;
            case RTFKeyword::LISTTEXT:
            // Should be ignored by any reader that understands Word 97 through Word 2007 numbering.
            case RTFKeyword::NONESTTABLES:
                // This destination should be ignored by readers that support nested tables.
                m_aStates.top().setDestination(Destination::SKIP);
                break;
            case RTFKeyword::DO:
                m_aStates.top().setDestination(Destination::DRAWINGOBJECT);
                break;
            case RTFKeyword::PN:
                m_aStates.top().setDestination(Destination::PARAGRAPHNUMBERING);
                break;
            case RTFKeyword::PNTEXT:
                // This destination should be ignored by readers that support paragraph numbering.
                m_aStates.top().setDestination(Destination::SKIP);
                break;
            case RTFKeyword::PNTXTA:
                m_aStates.top().setDestination(Destination::PARAGRAPHNUMBERING_TEXTAFTER);
                break;
            case RTFKeyword::PNTXTB:
                m_aStates.top().setDestination(Destination::PARAGRAPHNUMBERING_TEXTBEFORE);
                break;
            case RTFKeyword::TITLE:
                m_aStates.top().setDestination(Destination::TITLE);
                break;
            case RTFKeyword::SUBJECT:
                m_aStates.top().setDestination(Destination::SUBJECT);
                break;
            case RTFKeyword::DOCCOMM:
                m_aStates.top().setDestination(Destination::DOCCOMM);
                break;
            case RTFKeyword::ATRFSTART:
                m_aStates.top().setDestination(Destination::ANNOTATIONREFERENCESTART);
                break;
            case RTFKeyword::ATRFEND:
                m_aStates.top().setDestination(Destination::ANNOTATIONREFERENCEEND);
                break;
            case RTFKeyword::ATNID:
                m_aStates.top().setDestination(Destination::ATNID);
                break;
            case RTFKeyword::MMATH:
            case RTFKeyword::MOMATHPARA:
                // Nothing to do here (just enter the destination) till RTFKeyword::MMATHPR is implemented.
                break;
            case RTFKeyword::MR:
                m_aStates.top().setDestination(Destination::MR);
                break;
            case RTFKeyword::MCHR:
                m_aStates.top().setDestination(Destination::MCHR);
                break;
            case RTFKeyword::MPOS:
                m_aStates.top().setDestination(Destination::MPOS);
                break;
            case RTFKeyword::MVERTJC:
                m_aStates.top().setDestination(Destination::MVERTJC);
                break;
            case RTFKeyword::MSTRIKEH:
                m_aStates.top().setDestination(Destination::MSTRIKEH);
                break;
            case RTFKeyword::MDEGHIDE:
                m_aStates.top().setDestination(Destination::MDEGHIDE);
                break;
            case RTFKeyword::MTYPE:
                m_aStates.top().setDestination(Destination::MTYPE);
                break;
            case RTFKeyword::MGROW:
                m_aStates.top().setDestination(Destination::MGROW);
                break;
            case RTFKeyword::MHIDETOP:
            case RTFKeyword::MHIDEBOT:
            case RTFKeyword::MHIDELEFT:
            case RTFKeyword::MHIDERIGHT:
                // SmOoxmlImport::handleBorderBox will ignore these anyway, so silently ignore for now.
                m_aStates.top().setDestination(Destination::SKIP);
                break;
            case RTFKeyword::MSUBHIDE:
                m_aStates.top().setDestination(Destination::MSUBHIDE);
                break;
            case RTFKeyword::MSUPHIDE:
                m_aStates.top().setDestination(Destination::MSUPHIDE);
                break;
            case RTFKeyword::MBEGCHR:
                m_aStates.top().setDestination(Destination::MBEGCHR);
                break;
            case RTFKeyword::MSEPCHR:
                m_aStates.top().setDestination(Destination::MSEPCHR);
                break;
            case RTFKeyword::MENDCHR:
                m_aStates.top().setDestination(Destination::MENDCHR);
                break;
            case RTFKeyword::UPR:
                m_aStates.top().setDestination(Destination::UPR);
                break;
            case RTFKeyword::UD:
                // Anything inside \ud is just normal Unicode content.
                m_aStates.top().setDestination(Destination::NORMAL);
                break;
            case RTFKeyword::BACKGROUND:
                m_aStates.top().setDestination(Destination::BACKGROUND);
                m_aStates.top().setInBackground(true);
                break;
            case RTFKeyword::SHPGRP:
            {
                RTFLookahead aLookahead(Strm(), m_pTokenizer->getGroupStart());
                if (!aLookahead.hasTable())
                {
                    uno::Reference<drawing::XShapes> xGroupShape(
                        getTextDocument()->createInstance(u"com.sun.star.drawing.GroupShape"_ustr),
                        uno::UNO_QUERY);
                    if (m_xDstDoc)
                    {
                        uno::Reference<drawing::XShape> xShape(xGroupShape, uno::UNO_QUERY);
                        // set default VertOrient before inserting
                        uno::Reference<beans::XPropertySet>(xShape, uno::UNO_QUERY_THROW)
                            ->setPropertyValue(u"VertOrient"_ustr,
                                               uno::Any(text::VertOrientation::NONE));
                        m_xDstDoc->getDrawPage()->add(xShape);
                    }
                    m_pSdrImport->pushParent(xGroupShape);
                    m_aStates.top().setCreatedShapeGroup(true);
                }
                m_aStates.top().setDestination(Destination::SHAPEGROUP);
                m_aStates.top().setInShapeGroup(true);
            }
            break;
            case RTFKeyword::FTNSEP:
                m_aStates.top().setDestination(Destination::FOOTNOTESEPARATOR);
                m_aStates.top().getCharacterAttributes().set(
                    NS_ooxml::LN_CT_FtnEdn_type,
                    new RTFValue(NS_ooxml::LN_Value_doc_ST_FtnEdn_separator));
                break;
            case RTFKeyword::USERPROPS:
                // Container of all user-defined properties.
                m_aStates.top().setDestination(Destination::USERPROPS);
                if (m_xDocumentProperties.is())
                    // Create a custom document properties to be able to process them later all at once.
                    m_xDocumentProperties = document::DocumentProperties::create(m_xContext);
                break;
            case RTFKeyword::PROPNAME:
                m_aStates.top().setDestination(Destination::PROPNAME);
                break;
            case RTFKeyword::STATICVAL:
                m_aStates.top().setDestination(Destination::STATICVAL);
                break;
            case RTFKeyword::GENERATOR:
                m_aStates.top().setDestination(Destination::GENERATOR);
                break;
            default:
            {
                // Check if it's a math token.
                if (auto pSymbolData = RTFTokenizer::lookupMathKeyword(nKeyword))
                {
                    m_aMathBuffer.appendOpeningTag(pSymbolData->GetToken());
                    m_aStates.top().setDestination(pSymbolData->GetDestination());
                    return RTFError::OK;
                }

                SAL_INFO("writerfilter",
                         "TODO handle destination '" << RTFTokenizer::toString(nKeyword) << "'");
                // Make sure we skip destinations (even without \*) till we don't handle them
                m_aStates.top().setDestination(Destination::SKIP);
                aSkip.setParsed(false);
            }
            break;
        }

    // new destination => use new destination text
    m_aStates.top().setCurrentDestinationText(&m_aStates.top().getDestinationText());

    return RTFError::OK;
}

} // namespace writerfilter

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