/* -*- 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 .
 */
#pragma once

#include <scriptinfo.hxx>

#include <swrect.hxx>
#include <swtypes.hxx>
#include "portxt.hxx"
#include <svx/ctredlin.hxx>

#include <vector>
#include <deque>

class SwMarginPortion;
class SwDropPortion;
class SwTextFormatter;

class SwCharRange
{
private:
    TextFrameIndex m_nStart;
    TextFrameIndex m_nLen;

public:
    SwCharRange(TextFrameIndex const nInitStart = TextFrameIndex(0),
                TextFrameIndex const nInitLen = TextFrameIndex(0))
        : m_nStart( nInitStart ), m_nLen(nInitLen) {}
    TextFrameIndex      & Start()       { return m_nStart; }
    TextFrameIndex const& Start() const { return m_nStart; }
    void LeftMove(TextFrameIndex const nNew)
            { if ( nNew < m_nStart ) { m_nLen += m_nStart-nNew; m_nStart = nNew; } }
    TextFrameIndex      & Len()       { return m_nLen; }
    TextFrameIndex const& Len() const { return m_nLen; }
    bool operator<(const SwCharRange &rRange) const
                { return m_nStart < rRange.m_nStart; }
    bool operator>(const SwCharRange &rRange) const
                { return m_nStart + m_nLen > rRange.m_nStart + rRange.m_nLen; }
    bool operator!=(const SwCharRange &rRange) const
                { return *this < rRange || *this > rRange; }
    SwCharRange &operator+=(const SwCharRange &rRange);
};

// SwRepaint is a document-global SwRect
// nOfst states from where in the first line should be painted
// nRightOfst gives the right margin
class SwRepaint : public SwRect
{
    SwTwips m_nOffset;
    SwTwips m_nRightOffset;
public:
    SwRepaint() : SwRect(), m_nOffset( 0 ), m_nRightOffset( 0 ) {}

    SwTwips GetOffset() const { return m_nOffset; }
    void   SetOffset( const SwTwips nNew ) { m_nOffset = nNew; }
    SwTwips GetRightOfst() const { return m_nRightOffset; }
    void   SetRightOfst( const SwTwips nNew ) { m_nRightOffset = nNew; }
};

/// Collection of SwLinePortion instances, representing one line of text.
/// Typically owned by an SwParaPortion.
class SAL_DLLPUBLIC_RTTI SwLineLayout : public SwTextPortion
{
private:
    SwLineLayout *m_pNext;                // The next Line
    std::unique_ptr<std::vector<tools::Long>> m_pLLSpaceAdd;     // Used for justified alignment
    std::unique_ptr<std::deque<sal_uInt16>> m_pKanaComp;  // Used for Kana compression
    std::vector<TextFrameIndex> m_aKashida;
    SwTwips m_nRealHeight;             // The height resulting from line spacing and register
    SwTwips m_nTextHeight;             // The max height of all non-FlyCnt portions in this Line
    SwTwips m_nExtraAscent = 0;
    SwTwips m_nExtraDescent = 0;
    sal_Int32 m_nSpaceCount = 0;       // space count in the line
    SwTwips m_nLetterSpacing = 0;      // custom letter spacing
    SwTwips m_nScaleWidth = 100;       // glyph scaling (grain resolution to limit font generation)
    float m_fScaleWidthSpacing = 0.0;  // extra space filled by glyph scaling

    bool m_bFormatAdj : 1;
    bool m_bDummy     : 1;
    bool m_bEndHyph   : 1;
    bool m_bMidHyph   : 1;
    bool m_bLastHyph  : 1;
    bool m_bFly       : 1;
    bool m_bRest      : 1;
    bool m_bBlinking  : 1;
    bool m_bClipping  : 1; // Clipping needed for exact line height
    bool m_bContent   : 1; // Text for line numbering
    bool m_bRedline   : 1; // The Redlining
    bool m_bRedlineEnd: 1; // Redlining for paragraph mark: tracked change at the end
    bool m_bForcedLeftMargin : 1; // Left adjustment moved by the Fly
    bool m_bHanging : 1; // Contains a hanging portion in the margin

    enum RedlineType m_eRedlineEnd; // redline type of pilcrow and line break symbols

    OUString m_sRedlineText; // shortened text of (first) tracked deletion shown in margin

    SwTwips GetHangingMargin_() const;

    void DeleteNext();
public:
    // From SwPositiveSize
    using SwPositiveSize::Height;
    virtual void Height(const SwTwips nNew, const bool bText = true) override;

    // From SwLinePortion
    virtual SwLinePortion *Insert( SwLinePortion *pPortion ) override;
    virtual SwLinePortion *Append( SwLinePortion *pPortion ) override;
    SW_DLLPUBLIC SwLinePortion *GetFirstPortion() const;

    // Flags
    void ResetFlags();
    void SetFormatAdj( const bool bNew ) { m_bFormatAdj = bNew; }
    bool IsFormatAdj() const { return m_bFormatAdj; }
    void SetEndHyph( const bool bNew ) { m_bEndHyph = bNew; }
    bool IsEndHyph() const { return m_bEndHyph; }
    void SetMidHyph( const bool bNew ) { m_bMidHyph = bNew; }
    bool IsMidHyph() const { return m_bMidHyph; }
    void SetLastHyph( const bool bNew ) { m_bLastHyph = bNew; }
    bool IsLastHyph() const { return m_bLastHyph; }
    void SetFly( const bool bNew ) { m_bFly = bNew; }
    bool IsFly() const { return m_bFly; }
    void SetRest( const bool bNew ) { m_bRest = bNew; }
    bool IsRest() const { return m_bRest; }
    void SetBlinking( const bool bNew ) { m_bBlinking = bNew; }
    bool IsBlinking() const { return m_bBlinking; }
    void SetContent( const bool bNew ) { m_bContent = bNew; }
    bool HasContent() const { return m_bContent; }
    void SetRedline( const bool bNew ) { m_bRedline = bNew; }
    bool HasRedline() const { return m_bRedline; }
    void SetRedlineEnd( const bool bNew ) { m_bRedlineEnd = bNew; }
    bool HasRedlineEnd() const { return m_bRedlineEnd; }
    void SetRedlineEndType( const enum RedlineType eNew ) { m_eRedlineEnd = eNew; }
    RedlineType GetRedlineEndType() const { return m_eRedlineEnd; }
    void SetRedlineText ( const OUString& sText ) { m_sRedlineText = sText; }
    const OUString* GetRedlineText() const { return &m_sRedlineText; }
    void SetForcedLeftMargin() { m_bForcedLeftMargin = true; }
    bool HasForcedLeftMargin() const { return m_bForcedLeftMargin; }
    void SetHanging( const bool bNew ) { m_bHanging = bNew; }
    bool IsHanging() const { return m_bHanging; }

    // Respecting empty dummy lines
    void SetDummy( const bool bNew ) { m_bDummy = bNew; }
    bool IsDummy() const { return m_bDummy; }

    void SetClipping( const bool bNew ) { m_bClipping = bNew; }
    bool IsClipping() const { return m_bClipping; }

    SwLineLayout();
    virtual ~SwLineLayout() override;

    SwLineLayout *GetNext() { return m_pNext; }
    const SwLineLayout *GetNext() const { return m_pNext; }
    void SetNext( SwLineLayout *pNew ) { m_pNext = pNew; }

    void Init( SwLinePortion *pNextPortion = nullptr);

    // Collects the data for the line
    void CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf );

    void SetRealHeight( SwTwips nNew ) { m_nRealHeight = nNew; }
    SwTwips GetRealHeight() const { return m_nRealHeight; }

    SwTwips GetTextHeight() const { return m_nTextHeight; }

    void SetExtraAscent(SwTwips nNew) { m_nExtraAscent = nNew; }
    SwTwips GetExtraAscent() const { return m_nExtraAscent; }

    void SetExtraDescent(SwTwips nNew) { m_nExtraDescent = nNew; }
    SwTwips GetExtraDescent() const { return m_nExtraDescent; }

    void SetSpaceCount(sal_Int32 nNew) { m_nSpaceCount = nNew; }
    sal_Int32 GetSpaceCount() const { return m_nSpaceCount; }

    void SetLetterSpacing(SwTwips nNew) { m_nLetterSpacing = nNew; }
    SwTwips GetLetterSpacing() const { return m_nLetterSpacing; }

    void SetScaleWidth(SwTwips nNew) { m_nScaleWidth = nNew; }
    SwTwips GetScaleWidth() const { return m_nScaleWidth; }

    void SetScaleWidthSpacing(SwTwips nNew) { m_fScaleWidthSpacing = nNew; }
    float GetScaleWidthSpacing() const { return m_fScaleWidthSpacing; }

    // Creates the glue chain for short lines
    SwMarginPortion *CalcLeftMargin();

    SwTwips GetHangingMargin() const
        { return GetHangingMargin_(); }

    // For special treatment for empty lines
    virtual bool Format( SwTextFormatInfo &rInf ) override;

    // Stuff for justified alignment
    bool IsSpaceAdd() const { return m_pLLSpaceAdd != nullptr; }
    void InitSpaceAdd();     // Creates pLLSpaceAdd if necessary
    void CreateSpaceAdd( const tools::Long nInit = 0 );
    void FinishSpaceAdd() { m_pLLSpaceAdd.reset(); }
    sal_uInt16 GetLLSpaceAddCount() const { return sal::static_int_cast< sal_uInt16 >(m_pLLSpaceAdd->size()); }
    void SetLLSpaceAdd( tools::Long nNew, sal_uInt16 nIdx )
    {
        if ( nIdx == GetLLSpaceAddCount() )
            m_pLLSpaceAdd->push_back( nNew );
        else
            (*m_pLLSpaceAdd)[ nIdx ] = nNew;
    }
    tools::Long GetLLSpaceAdd( sal_uInt16 nIdx ) { return (*m_pLLSpaceAdd)[ nIdx ]; }
    void RemoveFirstLLSpaceAdd() { m_pLLSpaceAdd->erase( m_pLLSpaceAdd->begin() ); }
    std::vector<tools::Long>* GetpLLSpaceAdd() const { return m_pLLSpaceAdd.get(); }

    // Stuff for Kana compression
    void SetKanaComp( std::unique_ptr<std::deque<sal_uInt16>> pNew ){ m_pKanaComp = std::move(pNew); }
    void FinishKanaComp() { m_pKanaComp.reset(); }
    std::deque<sal_uInt16>* GetpKanaComp() const { return m_pKanaComp.get(); }
    std::deque<sal_uInt16>& GetKanaComp() { return *m_pKanaComp; }

    void SetKashida(std::vector<TextFrameIndex> aNew) { m_aKashida = std::move(aNew); }
    std::span<const TextFrameIndex> GetKashida() const { return m_aKashida; }

    /** determine ascent and descent for positioning of as-character anchored
        object

        OD 07.01.2004 #i11859# - previously local method <lcl_MaxAscDescent>
        Method calculates maximum ascents and descents of the line layout.
        One value considering as-character anchored objects, one without these
        objects.
        Portions for other anchored objects aren't considered.
        OD 2005-05-20 #i47162# - add optional parameter <_bNoFlyCntPorAndLinePor>
        to control, if the fly content portions and line portion are considered.

        @param _orAscent
        output parameter - maximum ascent without as-character anchored objects

        @param _orDescent
        output parameter - maximum descent without as-character anchored objects

        @param _orObjAscent
        output parameter - maximum ascent with as-character anchored objects

        @param _orObjDescent
        output parameter - maximum descent with as-character anchored objects

        @param _pDontConsiderPortion
        input parameter - portion, which isn't considered for calculating
        <_orObjAscent> and <_orObjDescent>, if it isn't a portion for a
        as-character anchored object or it isn't as high as the line.

        @param _bNoFlyCntPorAndLinePor
        optional input parameter - boolean, indicating that fly content portions
        and the line portion are considered or not.
    */
    void MaxAscentDescent( SwTwips& _orAscent,
                           SwTwips& _orDescent,
                           SwTwips& _orObjAscent,
                           SwTwips& _orObjDescent,
                           const SwLinePortion* _pDontConsiderPortion = nullptr,
                           const bool _bNoFlyCntPorAndLinePor = false ) const;

    void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
                   TextFrameIndex& nOffset) const override;
};

/// Collection of SwLineLayout instances, represents the paragraph text in Writer layout.
/// Typically owned by an SwTextFrame.
class SwParaPortion : public SwLineLayout
{
    // Area that needs repainting
    SwRepaint m_aRepaint;
    // Area that needs reformatting
    SwCharRange m_aReformat;
    SwScriptInfo m_aScriptInfo;

    // Fraction aZoom;
    tools::Long m_nDelta;

    // If a SwTextFrame is locked, no changes occur to the formatting data (under
    // pLine) (compare with Orphans)
    bool m_bFlys          : 1; // Overlapping Flys?
    bool m_bPrep          : 1; // PREP_*
    bool m_bPrepWidows    : 1; // PrepareHint::Widows
    bool m_bPrepAdjust    : 1; // PrepareHint::AdjustSizeWithoutFormatting
    bool m_bPrepMustFit   : 1; // PrepareHint::MustFit
    bool m_bFollowField   : 1; // We have a bit of field left for the Follow

    bool m_bFixLineHeight : 1; // Fixed line height
    bool m_bFootnoteNum   : 1; // is the frame that may contain a footnotenumberportion
    bool m_bMargin        : 1; // contains a hanging punctuation in the margin

public:
    SwParaPortion();
    virtual ~SwParaPortion() override;

    // Resets all formatting information (except for bFlys)
    inline void FormatReset();

    // Resets the Flags
    inline void ResetPreps();

    // Get/Set methods
    SwRepaint& GetRepaint() { return m_aRepaint; }
    const SwRepaint& GetRepaint() const { return m_aRepaint; }
    SwCharRange& GetReformat() { return m_aReformat; }
    const SwCharRange& GetReformat() const { return m_aReformat; }
    void SetDelta(tools::Long nDelta) { m_nDelta = nDelta; }
    tools::Long GetDelta() const { return m_nDelta; }
    SwScriptInfo& GetScriptInfo() { return m_aScriptInfo; }
    const SwScriptInfo& GetScriptInfo() const { return m_aScriptInfo; }

    // For SwTextFrame::Format: returns the paragraph's current length
    TextFrameIndex GetParLen() const;

    // For Prepare()
    bool UpdateQuoVadis( std::u16string_view rQuo );

    // Flags
    void SetFly() { m_bFlys = true; }
    bool HasFly() const { return m_bFlys; }

    // Preps
    void SetPrep() { m_bPrep = true; }
    bool IsPrep() const { return m_bPrep; }
    void SetPrepWidows() { m_bPrepWidows = true; }
    bool IsPrepWidows() const { return m_bPrepWidows; }
    void SetPrepMustFit( const bool bNew ) { m_bPrepMustFit = bNew; }
    bool IsPrepMustFit() const { return m_bPrepMustFit; }
    void SetPrepAdjust() { m_bPrepAdjust = true; }
    bool IsPrepAdjust() const { return m_bPrepAdjust; }
    void SetFollowField( const bool bNew ) { m_bFollowField = bNew; }
    bool IsFollowField() const { return m_bFollowField; }
    void SetFixLineHeight() { m_bFixLineHeight = true; }
    bool IsFixLineHeight() const { return m_bFixLineHeight; }

    void SetFootnoteNum( const bool bNew ) { m_bFootnoteNum = bNew; }
    bool IsFootnoteNum() const { return m_bFootnoteNum; }
    void SetMargin( const bool bNew = true ) { m_bMargin = bNew; }
    bool IsMargin() const { return m_bMargin; }
    enum FootnoteOrNot { OnlyNumbering, FootnoteToo };
    bool HasNumberingPortion(FootnoteOrNot) const;
    bool HasContentPortions() const;

    // Set nErgo in the QuoVadisPortion
    void SetErgoSumNum( const OUString &rErgo );

    const SwDropPortion *FindDropPortion() const;

    void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
                   TextFrameIndex& nOffset) const override;
};

inline void SwParaPortion::ResetPreps()
{
    m_bPrep = m_bPrepWidows = m_bPrepAdjust = m_bPrepMustFit = false;
}

inline void SwParaPortion::FormatReset()
{
    m_nDelta = 0;
    m_aReformat = SwCharRange(TextFrameIndex(0), TextFrameIndex(COMPLETE_STRING));
    // bFlys needs to be retained in SwTextFrame::Format_() so that empty
    // paragraphs that needed to avoid Frames with no flow, reformat
    // when the Frame disappears from the Area
    // bFlys = false;
    ResetPreps();
    m_bFollowField = m_bFixLineHeight = m_bMargin = false;
}

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