/* This file is part of the KDE project
   SPDX-FileCopyrightText: 2003 Lukas Tinkl <lukas@kde.org>
   SPDX-FileCopyrightText: 2003 David Faure <faure@kde.org>

   SPDX-License-Identifier: LGPL-2.0-or-later
*/

#ifndef KOSTYLESTACK_H
#define KOSTYLESTACK_H


#include <QList>
#include <QStack>
#include <QPair>

#include "koodf_export.h"
#include <KoXmlReader.h>

/**
 *  @brief This class implements a stack for the different styles of an object.
 *
 *  There can be several styles that are valid for one object. For example
 *  a textobject on a page has styles 'pr3' and 'P7' and a paragraph in
 *  that textobject has styles 'P1' and 'T3'. And some styles even have
 *  parent-styles...
 *
 *  If you want to know if there is, for example,  the attribute 'fo:font-family'
 *  for this paragraph, you have to look into style 'T3', 'P1', 'P7' and 'pr3'.
 *  When you find this attribute in one style you have to stop processing the list
 *  and take the found attribute for this object.
 *
 *  This is what this class does. You can push styles on the stack while walking
 *  through the xml-tree to your object and then ask the stack if any of the styles
 *  provides a certain attribute. The stack will search from top to bottom, i.e.
 *  in our example from 'T3' to 'pr3' and return the first occurrence of the wanted
 *  attribute.
 *
 *  So this is some sort of inheritance where the styles on top of the stack overwrite
 *  the same attribute of a lower style on the stack.
 *
 *  In general though, you wouldn't use push/pop directly, but KoOdfLoadingContext::fillStyleStack
 *  or KoOdfLoadingContext::addStyles to automatically push a style and all its
 *  parent styles onto the stack.
 */
class KOODF_EXPORT KoStyleStack
{
public:
    /**
     * Create a OASIS style stack
     */
    KoStyleStack();
    /**
     * Create a style stack based on other namespaces than OASIS - used for OOo-1.1 import.
     */
    explicit KoStyleStack(const char* styleNSURI, const char* foNSURI);
    virtual ~KoStyleStack();

    /**
     * Clears the complete stack.
     */
    void clear();

    /**
     * Save the current state of the stack. Any items added between
     * this call and its corresponding restore() will be removed when calling restore().
     */
    void save();

    /**
     * Restore the stack to the state it was at the corresponding save() call.
     */
    void restore();

    /**
     * Removes the style on top of the stack.
     */
    void pop();

    /**
     * Pushes the new style onto the stack.
     */
    void push(const KoXmlElement& style);

    /**
     * Check if any of the styles on the stack has an attribute called 'localName'
     */
    bool hasProperty(const QString &nsURI, const QString &localName) const;
    bool hasProperty(const QString &nsURI, const char *localName) const;

    /**
     * Check if any of the styles on the stack has an attribute called 'localname'-'detail'
     * where detail is e.g. left, right, top or bottom.
     * This allows to also find 'name' alone (e.g. padding implies padding-left, padding-right etc.)
     */
    bool hasProperty(const QString &nsURI, const QString &localName, const QString &detail) const;
    bool hasProperty(const QString &nsURI, const char *localName, const QString &detail) const;

    /**
     * Search for the attribute called 'localName', starting on top of the stack,
     * and return it.
     */
    QString property(const QString &nsURI, const QString &localName) const;
    QString property(const QString &nsURI, const char *localName) const;

    /**
     * Search for the attribute called 'localName'-'detail', starting on top of the stack,
     * and return it, where detail is e.g. left, right, top or bottom.
     * This allows to also find 'name' alone (e.g. padding implies padding-left, padding-right etc.)
     */
    QString property(const QString &nsURI, const QString &localName, const QString &detail) const;
    QString property(const QString &nsURI, const char *localName, const QString &detail) const;
    QString property(const QString &nsURI, const char *localName, const char *detail) const;

    /**
     * Check if any of the styles on the stack has a child element called 'localName' in the namespace 'nsURI'.
     */
    bool hasChildNode(const QString &nsURI, const QString &localName) const;

    /**
     * Search for a child element which has a child element called 'localName'
     * in the namespace 'nsURI' starting on top of the stack,
     * and return it.
     */
    KoXmlElement childNode(const QString &nsURI, const QString &localName) const;

    /**
     * Special case for the current font size, due to special handling of fo:font-size="115%".
     * First item in the returned value contains the font point size and the second the got percent.
     */
    QPair<qreal,qreal> fontSize(const qreal defaultFontPointSize = 12.0) const;

    /**
     * Return the name of the style specified by the user,
     * i.e. not an auto style.
     * This is used to know e.g. which user-style is associated with the current paragraph.
     * There could be none though.
     */
    QString userStyleName(const QString& family) const;

    /**
     * Return the display name of the style specified by the user,
     * i.e. not an auto style
     */
    QString userStyleDisplayName(const QString& family) const;

    /**
     * Set the type of properties that will be looked for.
     * For instance setTypeProperties("paragraph") will make hasAttribute() and attribute()
     * look into "paragraph-properties".
     * If @p typeProperties is 0, the stylestack is reset to look for "properties"
     * as it does by default.
     */
    void setTypeProperties(const char* typeProperties);

    /**
     * Overloaded method to also set backup properties to search in
     *
     * If the list is graphic, paragraph it will search first in graphic-properties and then in paragraph-properties
     */
    void setTypeProperties(const QList<QString> &typeProperties);

private:
    bool isUserStyle(const KoXmlElement& e, const QString& family) const;

    inline bool hasProperty(const QString &nsURI, const QString &localName, const QString *detail) const;

    inline QString property(const QString &nsURI, const QString &localName, const QString *detail) const;

    /// For save/restore: stack of "marks". Each mark is an index in m_stack.
    QStack<int> m_marks;

    /**
     * We use QValueList instead of QValueStack because we need access to all styles
     * not only the top one.
     */
    QList<KoXmlElement> m_stack;

    QList<QString> m_propertiesTagNames;

    QString m_styleNSURI;
    QString m_foNSURI;

    class KoStyleStackPrivate;
    KoStyleStackPrivate * const d;

    // forbidden
    void operator=(const KoStyleStack&);
    KoStyleStack(const KoStyleStack&);
};

#endif /* KOSTYLESTACK_H */
