/* -*- 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/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <hintids.hxx>
#include <osl/diagnose.h>
#include <tools/date.hxx>
#include <tools/time.hxx>
#include <svl/fstathelper.hxx>
#include <unotools/moduleoptions.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/docfilt.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/paperinf.hxx>
#include <o3tl/deleter.hxx>
#include <officecfg/Office/Writer.hxx>
#include <node.hxx>
#include <docary.hxx>
#include <fmtanchr.hxx>
#include <fmtfsize.hxx>
#include <fmtpdsc.hxx>
#include <shellio.hxx>
#include <iodetect.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentDeviceAccess.hxx>
#include <IDocumentLinksAdministration.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentState.hxx>
#include <IDocumentStylePoolAccess.hxx>
#include <pam.hxx>
#include <editsh.hxx>
#include <undobj.hxx>
#include <swundo.hxx>
#include <swtable.hxx>
#include <tblafmt.hxx>
#include <tblsel.hxx>
#include <pagedesc.hxx>
#include <poolfmt.hxx>
#include <fltini.hxx>
#include <docsh.hxx>
#include <ndole.hxx>
#include <ndtxt.hxx>
#include <redline.hxx>
#include <swerror.h>
#include <pausethreadstarting.hxx>
#include <frameformats.hxx>
#include <utility>

using namespace ::com::sun::star;

static bool sw_MergePortions(SwNode* pNode, void *)
{
    if (pNode->IsTextNode())
    {
        pNode->GetTextNode()->FileLoadedInitHints();
    }
    return true;
}

void SwAsciiOptions::Reset()
{
    m_sFont.clear();
    m_eCRLF_Flag = GetSystemLineEnd();
    m_eCharSet = ::osl_getThreadTextEncoding();
    m_nLanguage = LANGUAGE_SYSTEM;
    m_bIncludeBOM = true;
    m_bIncludeHidden = officecfg::Office::Writer::FilterFlags::ASCII::IncludeHiddenText::get();
}

ErrCodeMsg SwReader::Read( const Reader& rOptions )
{
    // copy variables
    Reader* po = const_cast<Reader*>(&rOptions);
    po->m_pStream = mpStrm;
    po->m_pStorage  = mpStg;
    po->m_xStorage  = mxStg;
    po->m_bInsertMode = nullptr != mpCursor;
    po->m_bSkipImages = mbSkipImages;
    po->SetInPaste(IsInPaste());

    // if a Medium is selected, get its Stream
    if( nullptr != (po->m_pMedium = mpMedium ) &&
        !po->SetStrmStgPtr() )
    {
        po->SetReadUTF8( false );
        po->SetBlockMode( false );
        po->SetOrganizerMode( false );
        po->SetIgnoreHTMLComments( false );
        return ERR_SWG_FILE_FORMAT_ERROR;
    }

    ErrCodeMsg nError = ERRCODE_NONE;

    GetDoc();

    // while reading, do not call OLE-Modified
    Link<bool,void> aOLELink( mxDoc->GetOle2Link() );
    mxDoc->SetOle2Link( Link<bool,void>() );

    mxDoc->SetInReading( true );
    mxDoc->SetInXMLImport( dynamic_cast< XMLReader* >(po) !=  nullptr );
    mxDoc->SetInWriterfilterImport(mpMedium && mpMedium->GetFilter()
            && (mpMedium->GetFilter()->GetUserData() == FILTER_RTF
                || mpMedium->GetFilter()->GetUserData() == sRtfWH
                || mpMedium->GetFilter()->GetUserData() == FILTER_DOCX));

    SwPaM *pPam;
    if( mpCursor )
        pPam = mpCursor;
    else
    {
        // if the Reader was not called by a Shell, create a PaM ourselves
        pPam = new SwPaM( mxDoc->GetNodes().GetEndOfContent(), SwNodeOffset(-1) );
        // For Web documents the default template was set already by InitNew,
        // unless the filter is not HTML,
        // or a SetTemplateName was called in ConvertFrom.
        if( !mxDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) || ReadHTML != po || !po->mxTemplate.is()  )
            po->SetTemplate( *mxDoc );
    }

    // Pams are connected like rings; stop when we return to the 1st element
    SwPaM *pEnd = pPam;

    bool bReadPageDescs = false;
    bool const bDocUndo = mxDoc->GetIDocumentUndoRedo().DoesUndo();
    bool bSaveUndo = bDocUndo && mpCursor;
    if( bSaveUndo )
    {
        // the reading of the page template cannot be undone!
        bReadPageDescs = po->m_aOption.IsPageDescs();
        if( bReadPageDescs )
        {
            bSaveUndo = false;
            mxDoc->GetIDocumentUndoRedo().DelAllUndoObj();
        }
        else
        {
            mxDoc->GetIDocumentUndoRedo().ClearRedo();
            mxDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSDOKUMENT, nullptr );
        }
    }
    mxDoc->GetIDocumentUndoRedo().DoUndo(false);

    RedlineFlags eOld = mxDoc->getIDocumentRedlineAccess().GetRedlineFlags();
    RedlineFlags ePostReadRedlineFlags( RedlineFlags::Ignore );

    // Array of FlyFormats
    SwFrameFormatsV aFlyFrameArr;
    // only read templates? then ignore multi selection!
    bool bFormatsOnly = po->m_aOption.IsFormatsOnly();

    while( true )
    {
        std::unique_ptr<SwUndoInsDoc> pUndo;
        if( bSaveUndo )
            pUndo.reset(new SwUndoInsDoc( *pPam ));

        mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore );

        std::optional<SwPaM> pUndoPam;
        if( bDocUndo || mpCursor )
        {
            // set Pam to the previous node, so that it is not also moved
            const SwNode& rTmp = pPam->GetPoint()->GetNode();
            pUndoPam.emplace( rTmp, rTmp, SwNodeOffset(0), SwNodeOffset(-1) );
        }

        // store for now all Fly's
        if( mpCursor )
        {
            aFlyFrameArr.insert( aFlyFrameArr.end(), mxDoc->GetSpzFrameFormats()->begin(),
                mxDoc->GetSpzFrameFormats()->end() );
        }

        const sal_Int32 nSttContent = pPam->GetPoint()->GetContentIndex();

        // make sure the End position is correct for all Readers
        SwContentNode* pCNd = pPam->GetPointContentNode();
        sal_Int32 nEndContent = pCNd ? pCNd->Len() - nSttContent : 0;
        SwNodeIndex aEndPos( pPam->GetPoint()->GetNode(), 1 );

        mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );

        {
            // Performance mode: import all bookmarks names as defined in the document
            auto perfModeGuard = mxDoc->getIDocumentMarkAccess()->disableUniqueNameChecks();

            nError = po->Read(*mxDoc, msBaseURL, *pPam, maFileName);
        }

        // an ODF document may contain redline mode in settings.xml; save it!
        ePostReadRedlineFlags = mxDoc->getIDocumentRedlineAccess().GetRedlineFlags();

        mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore );

        if( ! nError.IsError() )     // set the End position already
        {
            --aEndPos;
            pCNd = aEndPos.GetNode().GetContentNode();
            if( !pCNd && nullptr == ( pCNd = SwNodes::GoPrevious( &aEndPos ) ))
                pCNd = SwNodes::GoNext(&aEndPos);

            const sal_Int32 nLen = pCNd->Len();
            if( nLen < nEndContent )
                nEndContent = 0;
            else
                nEndContent = nLen - nEndContent;
            pPam->GetPoint()->Assign( *pCNd, nEndContent );

            const SwStartNode* pTableBoxStart = pCNd->FindTableBoxStartNode();
            if ( pTableBoxStart )
            {
                SwTableBox* pBox = pTableBoxStart->GetTableBox();
                if ( pBox )
                {
                    mxDoc->ChkBoxNumFormat( *pBox, true );
                }
            }
        }

        if( mpCursor )
        {
            *pUndoPam->GetMark() = *pPam->GetPoint();
            pUndoPam->GetPoint()->Adjust(SwNodeOffset(1));
            SwNode& rNd = pUndoPam->GetPointNode();
            if( rNd.IsContentNode() )
                pUndoPam->GetPoint()->SetContent( nSttContent );

            bool bChkHeaderFooter = rNd.FindHeaderStartNode() ||
                                   rNd.FindFooterStartNode();

            // search all new Fly's, and store them as individual Undo Objects
            for( sw::FrameFormats<sw::SpzFrameFormat*>::size_type n = 0; n < mxDoc->GetSpzFrameFormats()->size(); ++n )
            {
                SwFrameFormat* pFrameFormat = (*mxDoc->GetSpzFrameFormats())[ n ];
                const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
                // ok, here IsAlive is a misnomer...
                if (!aFlyFrameArr.IsAlive(pFrameFormat))
                {
                    if (SwUndoInserts::IsCreateUndoForNewFly(rAnchor,
                                pUndoPam->GetPoint()->GetNodeIndex(),
                                pUndoPam->GetMark()->GetNodeIndex()))
                    {
                        if( bChkHeaderFooter &&
                            (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) &&
                            RES_DRAWFRMFMT == pFrameFormat->Which() )
                        {
                            // DrawObjects are not allowed in Headers/Footers!
                            pFrameFormat->DelFrames();
                            mxDoc->DelFrameFormat( pFrameFormat );
                            --n;
                        }
                        else
                        {
                            if( pFrameFormat->HasWriterListeners() )
                            {
                                // Draw-Objects create a Frame when being inserted; thus delete them
                                pFrameFormat->DelFrames();
                            }

                            if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId())
                            {
                                if( !rAnchor.GetAnchorNode() )
                                {
                                    pFrameFormat->MakeFrames();
                                }
                                else if( mpCursor )
                                {
                                    mxDoc->SetContainsAtPageObjWithContentAnchor( true );
                                }
                            }
                            else
                                pFrameFormat->MakeFrames();
                        }
                    }
                }
            }
            aFlyFrameArr.clear();

            mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
            if( mxDoc->getIDocumentRedlineAccess().IsRedlineOn() )
                mxDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, *pUndoPam ), true);
            else
                mxDoc->getIDocumentRedlineAccess().SplitRedline( *pUndoPam );
            mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore );
        }
        if( bSaveUndo )
        {
            mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
            pUndo->SetInsertRange(*pUndoPam, true);
            // UGLY: temp. enable undo
            mxDoc->GetIDocumentUndoRedo().DoUndo(true);
            mxDoc->GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
            mxDoc->GetIDocumentUndoRedo().DoUndo(false);
            mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore );
        }

        pUndoPam.reset();

        pPam = pPam->GetNext();
        if( pPam == pEnd )
            break;

        // only read templates? then ignore multi selection! Bug 68593
        if( bFormatsOnly )
            break;

        /*
         * !!! The Status of the Stream has to be reset directly. !!!
         *     When Seeking, the current Status-, EOF- and bad-Bit is set;
         *     nobody knows why
         */
        if( mpStrm )
        {
            mpStrm->Seek(0);
            mpStrm->ResetError();
        }
    }

    // fdo#52028: ODF file import does not result in MergePortions being called
    // for every attribute, since that would be inefficient.  So call it here.
    // This is only necessary for formats that may contain RSIDs (ODF,MSO).
    // It's too hard to figure out which nodes were inserted in Insert->File
    // case (redlines, flys, footnotes, header/footer) so just do every node.
    mxDoc->GetNodes().ForEach(&sw_MergePortions);

    mxDoc->SetInReading( false );
    mxDoc->SetInXMLImport( false );
    mxDoc->SetInXMLImport242(false);
    mxDoc->SetInWriterfilterImport(false);

    if (!mbSkipInvalidateNumRules)
    {
        mxDoc->InvalidateNumRules();
    }
    mxDoc->UpdateNumRule();
    mxDoc->ChkCondColls();
    mxDoc->SetAllUniqueFlyNames();
    // Clear unassigned cell styles, because they aren't needed anymore.
    mxDoc->GetCellStyles().clear();

    mxDoc->GetIDocumentUndoRedo().DoUndo(bDocUndo);
    if (!bReadPageDescs && bSaveUndo )
    {
        mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
        mxDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSDOKUMENT, nullptr );
        mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore );
    }

    // delete Pam if it was created only for reading
    if( !mpCursor )
    {
        delete pPam;          // open a new one

        // #i42634# Moved common code of SwReader::Read() and
        // SwDocShell::UpdateLinks() to new SwDoc::UpdateLinks():
    // ATM still with Update
        mxDoc->getIDocumentLinksAdministration().UpdateLinks();

        // not insert: set the redline mode read from settings.xml
        eOld = ePostReadRedlineFlags & ~RedlineFlags::Ignore;

        mxDoc->getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
    }

    mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
    mxDoc->SetOle2Link( aOLELink );

    if( mpCursor )                 // the document is now modified
        mxDoc->getIDocumentState().SetModified();
    // #i38810# - If links have been updated, the document
    // have to be modified. During update of links the OLE link at the document
    // isn't set. Thus, the document's modified state has to be set again after
    // the OLE link is restored - see above <mxDoc->SetOle2Link( aOLELink )>.
    if ( mxDoc->getIDocumentLinksAdministration().LinksUpdated() )
    {
        mxDoc->getIDocumentState().SetModified();
    }

    po->SetReadUTF8( false );
    po->SetBlockMode( false );
    po->SetOrganizerMode( false );
    po->SetIgnoreHTMLComments( false );
    po->SetInPaste(false);

    return nError;
}


SwReader::SwReader(SfxMedium& rMedium, OUString aFileName, SwDoc *pDocument)
    : SwDocFac(pDocument), mpStrm(nullptr), mpMedium(&rMedium), mpCursor(nullptr),
    maFileName(std::move(aFileName)), mbSkipImages(false)
{
    SetBaseURL( rMedium.GetBaseURL() );
    SetSkipImages( rMedium.IsSkipImages() );
}


// Read into an existing document
SwReader::SwReader(SvStream& rStrm, OUString aFileName, const OUString& rBaseURL, SwPaM& rPam)
    : SwDocFac(&rPam.GetDoc()), mpStrm(&rStrm), mpMedium(nullptr), mpCursor(&rPam),
    maFileName(std::move(aFileName)), mbSkipImages(false)
{
    SetBaseURL( rBaseURL );
}

SwReader::SwReader(SfxMedium& rMedium, OUString aFileName, SwPaM& rPam)
    : SwDocFac(&rPam.GetDoc()), mpStrm(nullptr), mpMedium(&rMedium),
    mpCursor(&rPam), maFileName(std::move(aFileName)), mbSkipImages(false)
{
    SetBaseURL( rMedium.GetBaseURL() );
}

SwReader::SwReader( uno::Reference < embed::XStorage > xStg, OUString aFilename, SwPaM &rPam )
    : SwDocFac(&rPam.GetDoc()), mpStrm(nullptr), mxStg( std::move(xStg) ), mpMedium(nullptr), mpCursor(&rPam), maFileName(std::move(aFilename)), mbSkipImages(false)
{
}

Reader::Reader()
  : m_aDateStamp( Date::EMPTY ),
    m_aTimeStamp( tools::Time::EMPTY ),
    m_aCheckDateTime( DateTime::EMPTY ),
    m_pStream(nullptr), m_pMedium(nullptr), m_bInsertMode(false),
    m_bTemplateBrowseMode(false), m_bReadUTF8(false), m_bBlockMode(false), m_bOrganizerMode(false),
    m_bHasAskTemplateName(false), m_bIgnoreHTMLComments(false), m_bSkipImages(false)
{
}

Reader::~Reader()
{
}

OUString Reader::GetTemplateName(SwDoc& /*rDoc*/) const
{
    return OUString();
}

// load the Filter template, set and release
SwDoc* Reader::GetTemplateDoc(SwDoc& rDoc)
{
    if( !m_bHasAskTemplateName )
    {
        SetTemplateName( GetTemplateName(rDoc) );
        m_bHasAskTemplateName = true;
    }

    if( m_aTemplateName.isEmpty() )
        ClearTemplate();
    else
    {
        INetURLObject aTDir( m_aTemplateName );
        const OUString aFileName = aTDir.GetMainURL( INetURLObject::DecodeMechanism::NONE );
        OSL_ENSURE( !aTDir.HasError(), "No absolute path for template name!" );
        DateTime aCurrDateTime( DateTime::SYSTEM );
        bool bLoad = false;

        // if the template is already loaded, check once-a-minute if it has changed
        if( !mxTemplate.is() || aCurrDateTime >= m_aCheckDateTime )
        {
            Date aTstDate( Date::EMPTY );
            tools::Time aTstTime( tools::Time::EMPTY );
            if( FStatHelper::GetModifiedDateTimeOfFile(
                            aTDir.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
                            &aTstDate, &aTstTime ) &&
                ( !mxTemplate.is() || m_aDateStamp != aTstDate || m_aTimeStamp != aTstTime ))
            {
                bLoad = true;
                m_aDateStamp = aTstDate;
                m_aTimeStamp = aTstTime;
            }

            // only one minute later check if it has changed
            m_aCheckDateTime = aCurrDateTime;
            m_aCheckDateTime += tools::Time( 0, 1 );
        }

        if (bLoad)
        {
            ClearTemplate();
            OSL_ENSURE( !mxTemplate.is(), "Who holds the template doc?" );

            // If the writer module is not installed,
            // we cannot create a SwDocShell. We could create a
            // SwWebDocShell however, because this exists always
            // for the help.
            if (SvtModuleOptions().IsWriterInstalled())
            {
                rtl::Reference<SwDocShell> pDocSh = new SwDocShell(SfxObjectCreateMode::INTERNAL);
                SfxObjectShellLock xDocSh = pDocSh.get();
                if (pDocSh->DoInitNew())
                {
                    mxTemplate = pDocSh->GetDoc();
                    mxTemplate->SetOle2Link( Link<bool,void>() );
                    // always FALSE
                    mxTemplate->GetIDocumentUndoRedo().DoUndo( false );
                    mxTemplate->getIDocumentSettingAccess().set(DocumentSettingId::BROWSE_MODE, m_bTemplateBrowseMode );
                    mxTemplate->RemoveAllFormatLanguageDependencies();

                    ReadXML->SetOrganizerMode( true );
                    SfxMedium aMedium( aFileName, StreamMode::NONE );
                    SwReader aRdr( aMedium, OUString(), mxTemplate.get() );
                    aRdr.Read( *ReadXML );
                    ReadXML->SetOrganizerMode( false );
                }
            }
        }

        OSL_ENSURE( !mxTemplate.is() || FStatHelper::IsDocument( aFileName ) || m_aTemplateName=="$$Dummy$$",
                "TemplatePtr but no template exist!" );
    }

    return mxTemplate.get();
}

bool Reader::SetTemplate( SwDoc& rDoc )
{
    bool bRet = false;

    GetTemplateDoc(rDoc);
    if( mxTemplate.is() )
    {
        rDoc.RemoveAllFormatLanguageDependencies();
        rDoc.ReplaceStyles( *mxTemplate );
        rDoc.getIDocumentFieldsAccess().SetFixFields(nullptr);
        bRet = true;
    }

    return bRet;
}

void Reader::ClearTemplate()
{
    mxTemplate.clear();
}

void Reader::SetTemplateName( const OUString& rDir )
{
    if( !rDir.isEmpty() && m_aTemplateName != rDir )
    {
        ClearTemplate();
        m_aTemplateName = rDir;
    }
}

void Reader::MakeHTMLDummyTemplateDoc()
{
    ClearTemplate();
    mxTemplate = new SwDoc;
    mxTemplate->getIDocumentSettingAccess().set(DocumentSettingId::BROWSE_MODE, m_bTemplateBrowseMode );
    mxTemplate->getIDocumentDeviceAccess().getPrinter( true );
    mxTemplate->RemoveAllFormatLanguageDependencies();
    m_aCheckDateTime = DateTime( Date( 1, 1, 2300 ) );  // year 2300 should be sufficient
    m_aTemplateName = "$$Dummy$$";
}

// Users that do not need to open these Streams / Storages,
// have to override this method
bool Reader::SetStrmStgPtr()
{
    OSL_ENSURE( m_pMedium, "Where is the Media??" );

    if( m_pMedium->IsStorage() )
    {
        if( SwReaderType::Storage & GetReaderType() )
        {
            m_xStorage = m_pMedium->GetStorage();
            return true;
        }
    }
    else
    {
        m_pStream = m_pMedium->GetInStream();
        if ( m_pStream && SotStorage::IsStorageFile(m_pStream) && (SwReaderType::Storage & GetReaderType()) )
        {
            m_pStorage = new SotStorage( *m_pStream );
            m_pStream = nullptr;
        }
        else if ( !(SwReaderType::Stream & GetReaderType()) )
        {
            m_pStream = nullptr;
            return false;
        }

        return true;
    }
    return false;
}

SwReaderType Reader::GetReaderType()
{
    return SwReaderType::Stream;
}

void Reader::SetFltName( const OUString& )
{
}

void Reader::ResetFrameFormatAttrs( SfxItemSet &rFrameSet )
{
    rFrameSet.Put( SvxLRSpaceItem(RES_LR_SPACE) );
    rFrameSet.Put( SvxULSpaceItem(RES_UL_SPACE) );
    rFrameSet.Put( SvxBoxItem(RES_BOX) );
}

void Reader::ResetFrameFormats( SwDoc& rDoc )
{
    sal_uInt16 const s_ids[3] = {
        RES_POOLFRM_FRAME, RES_POOLFRM_GRAPHIC, RES_POOLFRM_OLE
    };
    for (sal_uInt16 i : s_ids)
    {
        SwFrameFormat *const pFrameFormat = rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( i );

        pFrameFormat->ResetFormatAttr( RES_LR_SPACE );
        pFrameFormat->ResetFormatAttr( RES_UL_SPACE );
        pFrameFormat->ResetFormatAttr( RES_BOX );
    }
}

    // read the sections of the document, which is equal to the medium.
    // returns the count of it
size_t Reader::GetSectionList(SfxMedium&, std::vector<OUString>&) const
{
    return 0;
}

bool SwReader::HasGlossaries( const Reader& rOptions )
{
    // copy variables
    Reader* po = const_cast<Reader*>(&rOptions);
    po->m_pStream = mpStrm;
    po->m_pStorage  = mpStg;
    po->m_bInsertMode = false;

    // if a Medium is selected, get its Stream
    bool bRet = false;
    if(  nullptr == (po->m_pMedium = mpMedium ) || po->SetStrmStgPtr() )
        bRet = po->HasGlossaries();
    return bRet;
}

bool SwReader::ReadGlossaries( const Reader& rOptions,
                                SwTextBlocks& rBlocks, bool bSaveRelFiles )
{
    // copy variables
    Reader* po = const_cast<Reader*>(&rOptions);
    po->m_pStream = mpStrm;
    po->m_pStorage  = mpStg;
    po->m_bInsertMode = false;

    // if a Medium is selected, get its Stream
    bool bRet = false;
    if( nullptr == (po->m_pMedium = mpMedium ) || po->SetStrmStgPtr() )
        bRet = po->ReadGlossaries( rBlocks, bSaveRelFiles );
    return bRet;
}

bool Reader::HasGlossaries() const
{
    return false;
}

bool Reader::ReadGlossaries( SwTextBlocks&, bool ) const
{
    return false;
}

SwReaderType StgReader::GetReaderType()
{
    return SwReaderType::Storage;
}

/*
 * Writer
 */

/*
 * Constructors, Destructors are inline (inc/shellio.hxx).
 */

SwWriter::SwWriter(SvStream& rStrm, SwCursorShell &rShell, bool bInWriteAll)
    : m_pStrm(&rStrm), m_pMedium(nullptr), m_pOutPam(nullptr), m_pShell(&rShell),
    m_rDoc(*rShell.GetDoc()), m_bWriteAll(bInWriteAll)
{
}

SwWriter::SwWriter(SvStream& rStrm,SwDoc &rDocument)
    : m_pStrm(&rStrm), m_pMedium(nullptr), m_pOutPam(nullptr), m_pShell(nullptr), m_rDoc(rDocument),
    m_bWriteAll(true)
{
}

SwWriter::SwWriter(SvStream& rStrm, SwPaM& rPam, bool bInWriteAll)
    : m_pStrm(&rStrm), m_pMedium(nullptr), m_pOutPam(&rPam), m_pShell(nullptr),
    m_rDoc(rPam.GetDoc()), m_bWriteAll(bInWriteAll)
{
}

SwWriter::SwWriter( uno::Reference < embed::XStorage > xStg, SwDoc &rDocument)
    : m_pStrm(nullptr), m_xStg( std::move(xStg) ), m_pMedium(nullptr), m_pOutPam(nullptr), m_pShell(nullptr), m_rDoc(rDocument), m_bWriteAll(true)
{
}

SwWriter::SwWriter(SfxMedium& rMedium, SwCursorShell &rShell, bool bInWriteAll)
    : m_pStrm(nullptr), m_pMedium(&rMedium), m_pOutPam(nullptr), m_pShell(&rShell),
    m_rDoc(*rShell.GetDoc()), m_bWriteAll(bInWriteAll)
{
}

SwWriter::SwWriter(SfxMedium& rMedium, SwDoc &rDocument)
    : m_pStrm(nullptr), m_pMedium(&rMedium), m_pOutPam(nullptr), m_pShell(nullptr), m_rDoc(rDocument),
    m_bWriteAll(true)
{
}

static bool isFlyNode(const SwPaM& pam)
{
    return *pam.GetPoint() == *pam.GetMark()
           && (pam.GetPoint()->GetNode().IsOLENode() || pam.GetPoint()->GetNode().IsGrfNode());
}

ErrCodeMsg SwWriter::Write( WriterRef const & rxWriter, const OUString* pRealFileName )
{
    // #i73788#
    SwPauseThreadStarting aPauseThreadStarting;

    bool bHasMark = false;
    std::shared_ptr<SwUnoCursor> pTempCursor;
    SwPaM * pPam;

    rtl::Reference<SwDoc> xDoc;

    if ( m_pShell && !m_bWriteAll && m_pShell->IsTableMode() )
    {
        m_bWriteAll = true;
        xDoc = new SwDoc;

        // Copy parts of a table:
        // Create a table with the width of the original and copy the selected cells.
        // The sizes are corrected by ratio.

        // search the layout for cells
        SwSelBoxes aBoxes;
        GetTableSel( *m_pShell, aBoxes );
        const SwTableNode* pTableNd = static_cast<const SwTableNode*>(aBoxes[0]->GetSttNd()->StartOfSectionNode());
        SwNodeIndex aIdx( xDoc->GetNodes().GetEndOfExtras(), 2 );
        SwContentNode *pNd = aIdx.GetNode().GetContentNode();
        OSL_ENSURE( pNd, "Node not found" );
        SwPosition aPos( aIdx, pNd, 0 );
        pTableNd->GetTable().MakeCopy( *xDoc, aPos, aBoxes );
    }

    if( !m_bWriteAll && ( m_pShell || m_pOutPam ))
    {
        if( m_pShell )
            pPam = m_pShell->GetCursor();
        else
            pPam = m_pOutPam;

        SwPaM *pEnd = pPam;

        // 1st round: Check if there is a selection
        do
        {
            bHasMark = pPam->HasMark() || isFlyNode(*pPam);
            pPam = pPam->GetNext();
        } while (!bHasMark && pPam != pEnd);

        // if there is no selection, select the whole document
        if(!bHasMark)
        {
            if( m_pShell )
            {
                m_pShell->Push();
                m_pShell->SttEndDoc(true);
                m_pShell->SetMark();
                m_pShell->SttEndDoc(false);
            }
            else
            {
                pPam = new SwPaM( *pPam, pPam );
                pPam->Move( fnMoveBackward, GoInDoc );
                pPam->SetMark();
                pPam->Move( fnMoveForward, GoInDoc );
            }
        }
        // pPam is still the current Cursor !!
    }
    else
    {
        // no Shell or write-everything -> create a Pam
        SwDoc* pOutDoc = xDoc.is() ? xDoc.get() : &m_rDoc;
        pTempCursor = pOutDoc->CreateUnoCursor(
                SwPosition(pOutDoc->GetNodes().GetEndOfContent()), false);
        pPam = pTempCursor.get();
        if( pOutDoc->IsClipBoard() )
        {
            pPam->Move( fnMoveBackward, GoInDoc );
            pPam->SetMark();
            pPam->Move( fnMoveForward, GoInDoc );
        }
        else
        {
            pPam->SetMark();
            pPam->Move( fnMoveBackward, GoInDoc );
        }
    }

    rxWriter->m_bWriteAll = m_bWriteAll;
    SwDoc* pOutDoc = xDoc.is() ? xDoc.get() : &m_rDoc;

    // If the default PageDesc has still the initial value,
    // (e.g. if no printer was set) then set it to DIN A4.
    // #i37248# - Modifications are only allowed at a new document.
    // <pOutDoc> contains a new document, if <xDoc> is set - see above.
    if ( xDoc.is() && !pOutDoc->getIDocumentDeviceAccess().getPrinter( false ) )
    {
        const SwPageDesc& rPgDsc = pOutDoc->GetPageDesc( 0 );
        //const SwPageDesc& rPgDsc = *pOutDoc->GetPageDescFromPool( RES_POOLPAGE_STANDARD );
        const SwFormatFrameSize& rSz = rPgDsc.GetMaster().GetFrameSize();
        // Clipboard-Document is always created w/o printer; thus the
        // default PageDesc is always aug LONG_MAX !! Set then to DIN A4
        if( LONG_MAX == rSz.GetHeight() || LONG_MAX == rSz.GetWidth() )
        {
            SwPageDesc aNew( rPgDsc );
            SwFormatFrameSize aNewSz( rSz );
            Size a4(SvxPaperInfo::GetPaperSize( PAPER_A4 ));
            aNewSz.SetHeight( a4.Width() );
            aNewSz.SetWidth( a4.Height() );
            aNew.GetMaster().SetFormatAttr( aNewSz );
            pOutDoc->ChgPageDesc( 0, aNew );
        }
    }

    bool bLockedView(false);
    SwEditShell* pESh = pOutDoc->GetEditShell();
    if( pESh )
    {
        bLockedView = pESh->IsViewLocked();
        pESh->LockView( true );    //lock visible section
        pESh->StartAllAction();
    }

    std::unique_ptr<PurgeGuard, o3tl::default_delete<PurgeGuard>> xGuard(new PurgeGuard(*pOutDoc));

    pOutDoc->SetInWriting(true);
    ErrCodeMsg nError = ERRCODE_NONE;
    if( m_pMedium )
        nError = rxWriter->Write( *pPam, *m_pMedium, pRealFileName );
    else if( m_pStrm )
        nError = rxWriter->Write( *pPam, *m_pStrm, pRealFileName );
    else if( m_xStg.is() )
        nError = rxWriter->Write( *pPam, m_xStg, pRealFileName );
    pOutDoc->SetInWriting(false);

    xGuard.reset();

    if( pESh )
    {
        pESh->EndAllAction();
        pESh->LockView( bLockedView );
    }

    // If the selection was only created for printing, reset the old cursor before returning
    if( !m_bWriteAll && ( m_pShell || m_pOutPam ))
    {
        if(!bHasMark)
        {
            if( m_pShell )
                m_pShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
            else
                delete pPam;
        }
    }
    else
    {
        // Everything was written successfully? Tell the document!
        if ( !nError.IsError() && !xDoc.is() )
        {
            m_rDoc.getIDocumentState().ResetModified();
            // #i38810# - reset also flag, that indicates updated links
            m_rDoc.getIDocumentLinksAdministration().SetLinksUpdated( false );
        }
    }

    if ( xDoc.is() )
    {
        pTempCursor.reset();
        xDoc.clear();
        m_bWriteAll = false;
    }

    return nError;
}

bool SetHTMLTemplate( SwDoc & rDoc )
{
    // get template name of the Sfx-HTML-Filter !!!
    if( !ReadHTML->GetTemplateDoc(rDoc) )
        ReadHTML->MakeHTMLDummyTemplateDoc();

    bool bRet = ReadHTML->SetTemplate( rDoc );

    SwNodes& rNds = rDoc.GetNodes();
    SwNodeIndex aIdx( rNds.GetEndOfExtras(), 1 );
    SwContentNode* pCNd = SwNodes::GoNext(&aIdx);
    if( pCNd )
    {
        pCNd->SetAttr
            ( SwFormatPageDesc(rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_HTML, false) ) );
        pCNd->ChgFormatColl( rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false ));
    }

    return bRet;
}

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