/* This file is part of the wvWare 2 project
   SPDX-FileCopyrightText: 2002-2003 Werner Trobin <trobin@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02111-1307, USA.
*/

#ifndef LISTS_H
#define LISTS_H

#include "global.h"
#include "sharedptr.h"
#include "ustring.h"
#include "wv2_export.h"
#include <QString>
#include <utility>
#include <vector>

namespace wvWare
{
class STTBF;
class OLEStreamReader;
class StyleSheet;
class ListLevel;
class ListData;
class ListFormatOverride;
class ListFormatOverrideLVL;
class ListInfoProvider;
namespace Word97
{
struct FIB;
struct PAP;
struct CHP;
}

/**
 * Please refer to the documentation of the ListInfo::text method.
 */
struct ListText {
    ListText();
    ~ListText();

    UString text;
    SharedPtr<Word97::CHP> chp;
};

/**
 * This class provides an abstraction for the list information in
 * Word6 and Word97 (they are totally different).
 *
 * I'm providing a slim API here and there would be some more information
 * inside the internal structures. Please speak up if you're missing
 * something, don't try to hack around this artificial limitation (Werner)
 */
class ListInfo
{
public:
    enum ListType {
        BulletType,
        NumberType,
        PictureType,
        DefaultType
    };

    /**
     * @param current paragraph's PAP
     * @param paragraph mark's CHP
     * @param ListInfoProvider
     */
    ListInfo(Word97::PAP &pap, Word97::CHP &chp, ListInfoProvider &listInfoProvider);

    /**
     * The istd linked to the current list/level, istdNil (4095) if none (LSTF::rgistd)
     */
    U16 linkedIstd() const
    {
        return m_linkedIstd;
    }
    /**
     * Returns whether the list counter should be restarted in new sections. (LSTF::fRestartHdn)
     */
    bool restartingCounter() const
    {
        return m_restartingCounter;
    }

    /**
     * The list starts at this value at the current level (LVLF::iStartAt)
     */
    S32 startAt() const
    {
        return m_startAt.first;
    }
    /**
     * This flag tells you whether some list format override set a new start value.
     * If this method returns true you have to restart the counter (initialized
     * with the startAt value)
     */
    bool startAtOverridden() const
    {
        return m_startAt.second;
    }
    /**
     * Number format code (see ANLD) (LVLF::nfc)
     */
    U8 numberFormat() const
    {
        return m_numberFormat;
    }
    /**
     * Alignment of the list number (LVLF::jc)
     */
    U8 alignment() const
    {
        return m_alignment;
    }
    /**
     * (LVLF::fLegal)
     */
    bool isLegal() const
    {
        return m_isLegal;
    }
    /**
     * (LVLF::fNoRestart)
     */
    bool notRestarted() const
    {
        return m_notRestarted;
    }
    /**
     * (LVLF::fPrev)
     */
    bool prev() const
    {
        return m_prev;
    }
    /**
     * (LVLF::fPrevSpace)
     */
    bool prevSpace() const
    {
        return m_prevSpace;
    }
    /**
     * (LVLF::fWord6)
     */
    bool isWord6() const
    {
        return m_isWord6;
    }

    /**
     * The most important method, returning the text template and
     * the corresponding CHP.
     *
     * The returned string contains place holders for the real
     * list counter text.  The place holders are the values 0...8,
     * representing the corresponding list levels (pap->ilvl).
     * For example: (<0>, <1>,... represent the ASCII values 0,
     * 1,...): "<0>.<1>.<2>)"

     * The <0> should be replaced with the current counter value
     * of level 0, then we should display a '.', <1> should be the
     * counter value of level 1, and so forth.
     *
     * The CHP provides the character formatting properties for
     * the list label.  Information about the alignment and
     * optional spaces/tabs after the counter text is also
     * available here (alignment, followingChar,...)
     */
    const ListText &text() const
    {
        return m_text;
    }

    /**
     * The character following the list number (LVLF::ixchFollow)
     */
    U8 followingChar() const
    {
        return m_followingChar;
    }

    /**
     * In order to help users to detect when a new list starts we also
     * provide access to the (internal) unique ID of a list.
     * @return 0 if it hasn't been initialized.
     */
    S32 lsid() const
    {
        return m_lsid;
    }

    /**
     * The space after the label.
     */
    U16 space() const
    {
        return m_space;
    }

    /**
     * The indent before the label.
     */
    U16 indent() const
    {
        return m_indent;
    }

    /**
     * Debugging...
     */
    void dump() const;

    /**
     * Set the name of the bullet picture.
     */
    void setBulletPictureName(const QString &name)
    {
        m_picName = name;
    };

    /**
     * @return bullet picture name.
     */
    QString bulletPictureName() const
    {
        return m_picName;
    };

    /**
     * @return whether the size of the picture changes automatically to
     * match the size of the text that follows the bullet
     */
    bool isBulletPictureAutoSize() const
    {
        return m_picAutoSize;
    };

    /**
     * @return list type.
     */
    ListType type() const
    {
        return m_type;
    };

private:
    U16 m_linkedIstd;
    bool m_restartingCounter;
    std::pair<S32, bool> m_startAt;
    U8 m_numberFormat;
    U8 m_alignment;
    bool m_isLegal;
    bool m_notRestarted;
    bool m_prev;
    bool m_prevSpace;
    bool m_isWord6;
    ListText m_text;
    U8 m_followingChar;
    S32 m_lsid;
    U16 m_space;
    U16 m_indent;
    QString m_picName;
    bool m_picAutoSize;
    ListType m_type;
};

/**
 * An internal class managing the list information read from the file.
 * Word versions before 8 don't have any explicit list information in
 * the table stream, it's just a part of the PAP (and it's way less
 * powerful). An object of this class will act as proxy for that
 * information in this case.
 */
class ListInfoProvider
{
    friend class ListInfo;

public:
    /**
     * This constructor assumes that this is a list info provider
     * for a Word 6 document. It will just act as a proxy for the information
     * inside pap.anld. To make that work you have to set the ANLD structure
     * every time the PAP changes, using setWord6StylePAP()
     */
    explicit ListInfoProvider(const StyleSheet *styleSheet);
    /**
     * This constructor reads the structures from the table stream of a
     * Word 97 document.
     */
    ListInfoProvider(OLEStreamReader *tableStream, const Word97::FIB &fib, const StyleSheet *styleSheet);
    ~ListInfoProvider();

    /**
     * Returns true if the passed ilfo is in a valid range.
     */
    bool isValid(S16 ilfo, U8 nLvlAnm) const;

private:
    ListInfoProvider(const ListInfoProvider &rhs) = delete;
    ListInfoProvider &operator=(const ListInfoProvider &rhs) = delete;

    /**
     * @internal
     * This function tries to get the list information (if any) from the
     * given PAP. As the list info handling is completely different in
     * Word6 and in Word97 we have one additional level of indirection here
     * (to keep the user of the library from fighting multiple versions).
     * In case there's no list information for that paragraph it returns
     * false.
     *
     * Take care, these methods modify the passed PAP structure!!
     */
    bool setPAP(Word97::PAP *pap);

    void readListData(OLEStreamReader *tableStream, const U32 endOfLSTF);
    void readListFormatOverride(OLEStreamReader *tableStream);
    void readListNames(OLEStreamReader *tableStream);
    void eatLeading0xff(OLEStreamReader *tableStream);

    void processOverride(ListFormatOverride *lfo);
    void convertCompatANLD();
    ListData *findLST(S32 lsid);

    const ListLevel *formattingListLevel() const;
    std::pair<S32, bool> startAt();
    ListText text(const Word97::CHP &chp) const;

    std::vector<ListData *> m_listData;
    std::vector<ListFormatOverride *> m_listFormatOverride;
    STTBF *m_listNames;

    Word97::PAP *m_pap; // we don't own that one!
    const StyleSheet *const m_styleSheet; // needed to determine the CHP

    ListFormatOverrideLVL *m_currentLfoLVL;
    const ListData *m_currentLst;

    // We have to keep track of the version, as Word radically changed
    // it's way to handle lists from version 7 to 8.
    const WordVersion m_version;
};

} // namespace wvWare

#endif // LISTS_H
