// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef SEEN_SP_STYLE_INTERNAL_H
#define SEEN_SP_STYLE_INTERNAL_H

/** \file
 * SPStyle internal: classes that are internal to SPStyle
 */
/* Authors:
 *   Lauris Kaplinski <lauris@kaplinski.com>
 *   Jon A. Cruz <jon@joncruz.org>
 *   Tavmjong Bah <tavmjong@free.fr>
 *
 * Copyright (C) 2014, 2018 Tavmjong Bah
 * Copyright (C) 2010 Jon A. Cruz
 * Copyright (C) 2001-2002 Lauris Kaplinski
 * Copyright (C) 2001 Ximian, Inc.
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include <utility>
#include <vector>
#include <map>

#include "style-enums.h"

#include "color.h"

#include "object/sp-marker-loc.h"
#include "object/sp-filter.h"
#include "object/sp-filter-reference.h"
#include "object/sp-paint-server-reference.h"
#include "object/uri.h"

#include "svg/svg-icc-color.h"

#include "xml/repr.h"


struct SPStyleEnum;

static const unsigned SP_STYLE_FLAG_ALWAYS (1 << 2);
static const unsigned SP_STYLE_FLAG_IFSET  (1 << 0);
static const unsigned SP_STYLE_FLAG_IFDIFF (1 << 1);
static const unsigned SP_STYLE_FLAG_IFSRC  (1 << 3); // If source matches

enum SPStyleSrc {
    SP_STYLE_SRC_UNSET,
    SP_STYLE_SRC_ATTRIBUTE,   // fill="red"
    SP_STYLE_SRC_STYLE_PROP,  // style="fill:red"
    SP_STYLE_SRC_STYLE_SHEET, // .red { fill:red; }
};

/* General comments:
 *
 * This code is derived from the original C style code in style.cpp.
 *
 * Overview:
 *   Style can be obtained (in order of precedence) [CHECK]
 *     1. "style" property in an element (style="fill:red").
 *     2. Style sheet, internal or external (<style> rect {fill:red;}</style>). 
 *     3. Attributes in an element (fill="red").
 *     4. Parent's style.
 *   A later property overrides an earlier property. This is implemented by
 *   reading in the properties backwards. If a property is already set, it
 *   prevents an earlier property from being read.
 *
 *   A declaration with an "!important" rule overrides any other declarations (except those that
 *   also have an "!important" rule). Attributes can not use the "!important" rule and the rule
 *   is not inherited.
 *
 *   In order for cascading to work, each element in the tree must be read in from top to bottom
 *   (parent before child). At each step, if a style property is not explicitly set, the property
 *   value is taken from the parent. Some properties have "computed" values that depend on:
 *      the parent's value (e.g. "font-size:larger"),
 *      another property value ("stroke-width":1em"), or
 *      an external value ("stroke-width:5%").
 *
 * To summarize:
 *
 *   An explicitly set value (including 'inherit') has a 'true' "set" flag.
 *   The "value" is either explicitly set or inherited.
 *   The "computed" value (if present) is calculated from "value" and some other input. 
 *
 * Functions:
 *   write():    Write a property and its value to a string.
 *     Flags:
 *       ALWAYS: Always write out property.
 *       IFSET:  Write a property if 'set' flag is true, otherwise return empty string.
 *       IFDIFF: Write a property if computed values are different, otherwise return empty string,
 *               This is only used for text!!
 *       IFSRC   Write a property if the source matches the requested source (style sheet, etc.).
 *
 *   read():     Set a property value from a string.
 *   clear():    Set a property to its default value and set the 'set' flag to false.
 *   cascade():  Cascade the parent's property values to the child if the child's property
 *               is unset (and it allows inheriting) or the value is 'inherit'.
 *               Calculate computed values that depend on parent.
 *               This requires that the parent already be updated.
 *   merge():    Merge the property values of a child and a parent that is being deleted,
 *               attempting to preserve the style of the child.
 *   operator=:  Assignment operator required due to use of templates (in original C code).
 *   operator==: True if computed values are equal.  TO DO: DEFINE EXACTLY WHAT THIS MEANS
 *   operator!=: Inverse of operator==.
 *
 *
 * Outside dependencies:
 *
 *   The C structures that these classes are evolved from were designed to be embedded in to the
 *   style structure (i.e they are "internal" and thus have an "I" in the SPI prefix). However,
 *   they should be reasonably stand-alone and can provide some functionality outside of the style
 *   structure (i.e. reading and writing style strings). Some properties do need access to other
 *   properties from the same object (e.g. SPILength sometimes needs to know font size) to
 *   calculate 'computed' values. Inheritance, of course, requires access to the parent object's
 *   style class.
 *
 *   The only real outside dependency is SPObject... which is needed in the cases of SPIPaint and
 *   SPIFilter for setting up the "href". (Currently, SPDocument is needed but this dependency
 *   should be removed as an "href" only needs the SPDocument for attaching an external document to
 *   the XML tree [see uri-references.cpp]. If SPDocument is really needed, it can be obtained from
 *   SPObject.)
 *
 */

/// Virtual base class for all SPStyle internal classes
class SPIBase
{

public:
    SPIBase( Glib::ustring name, bool inherits = true )
        : name(std::move(name)),
          inherits(inherits),
          set(false),
          inherit(false),
          important(false),
          style_src(SP_STYLE_SRC_STYLE_PROP), // Default to property, see bug 1662285.
          style(nullptr)
    {}

    virtual ~SPIBase()
    = default;

    virtual void read( gchar const *str ) = 0;
    virtual void readIfUnset( gchar const *str, SPStyleSrc const &source = SP_STYLE_SRC_STYLE_PROP ) {
        if (!str) return;

        bool has_important = false;
        Glib::ustring stripped = strip_important(str, has_important); // Sets 'has_important'
        // '!important' is invalid on attributes, don't read.
        if (source == SP_STYLE_SRC_ATTRIBUTE && has_important){
            return;
        }

        if ( !set || (has_important && !important) ) {
            read( stripped.c_str() );
            if ( set ) {
                style_src = source;
                if (has_important) {
                    important = true;
                }
            }
        }
    }

    Glib::ustring important_str() const {
        return Glib::ustring(important ? " !important" : "");
    }

    Glib::ustring strip_important( gchar const *str, bool &important ) {
        assert (str != NULL);
        Glib::ustring string = Glib::ustring(str);
        auto pos = string.rfind( " !important" );
        important = false;
        if (pos != std::string::npos) {
            important = true;
            string.erase(pos);
        }
        return string;
    }

    virtual void readAttribute( Inkscape::XML::Node *repr ) {
        readIfUnset( repr->attribute( name.c_str() ), SP_STYLE_SRC_ATTRIBUTE );
    }

    virtual const Glib::ustring get_value() const = 0;
    virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
                                       SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
                                       SPIBase const *const base = nullptr ) const;
    virtual void clear() {
        set = false, inherit = false, important = false;
    }

    virtual void cascade( const SPIBase* const parent ) = 0;
    virtual void merge(   const SPIBase* const parent ) = 0;

    virtual void setStylePointer( SPStyle *style_in  ) {
        style = style_in;
    }

    // Explicit assignment operator required due to templates.
    SPIBase& operator=(const SPIBase& rhs) = default;

    // Check apples being compared to apples
    virtual bool operator==(const SPIBase& rhs) {
        return (name == rhs.name);
    }

    virtual bool operator!=(const SPIBase& rhs) {
        return !(*this == rhs);
    }

  // To do: make private
public:
    Glib::ustring name;       // Make const
    unsigned inherits : 1;    // Property inherits by default from parent.
    unsigned set : 1;         // Property has been explicitly set (vs. inherited).
    unsigned inherit : 1;     // Property value set to 'inherit'.
    unsigned important : 1;   // Property rule 'important' has been explicitly set.
    SPStyleSrc style_src : 2; // Source (attribute, style attribute, style-sheet).

  // To do: make private after g_asserts removed
public:
    SPStyle* style;       // Used by SPIPaint, SPIFilter... to find values of other properties
};

/// Float type internal to SPStyle. (Only 'stroke-miterlimit')
class SPIFloat : public SPIBase
{

public:
    SPIFloat()
        : SPIBase( "anonymous_float" ),
          value(0.0)
    {}

    SPIFloat( Glib::ustring const &name, float value_default  = 0.0 )
        : SPIBase( name ),
          value(value_default),
          value_default(value_default)
    {}

    ~SPIFloat() override = default;
    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        value = value_default;
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIFloat& operator=(const SPIFloat& rhs) = default;

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

  // To do: make private
public:
    float value;

private:
    float value_default;
};

/*
 * One might think that the best value for SP_SCALE24_MAX would be ((1<<24)-1), which allows the
 * greatest possible precision for fitting [0, 1] fractions into 24 bits.
 *
 * However, in practice, that gives a problem with 0.5, which falls half way between two fractions
 * of ((1<<24)-1).  What's worse is that casting double(1<<23) / ((1<<24)-1) to float on x86
 * produces wrong rounding behaviour, resulting in a fraction of ((1<<23)+2.0f) / (1<<24) rather
 * than ((1<<23)+1.0f) / (1<<24) as one would expect, let alone ((1<<23)+0.0f) / (1<<24) as one
 * would ideally like for this example.
 *
 * The value (1<<23) is thus best if one considers float conversions alone.
 *
 * The value 0xff0000 can exactly represent all 8-bit alpha channel values,
 * and can exactly represent all multiples of 0.1.  I haven't yet tested whether
 * rounding bugs still get in the way of conversions to & from float, but my instinct is that
 * it's fairly safe because 0xff fits three times inside float's significand.
 *
 * We should probably use the value 0xffff00 once we support 16 bits per channel and/or LittleCMS,
 * though that might need to be accompanied by greater use of double instead of float for
 * colours and opacities, to be safe from rounding bugs.
 */
static const unsigned SP_SCALE24_MAX = 0xff0000;
#define SP_SCALE24_TO_FLOAT(v) ((double) (v) / SP_SCALE24_MAX)
#define SP_SCALE24_FROM_FLOAT(v) unsigned(((v) * SP_SCALE24_MAX) + .5)

/** Returns a scale24 for the product of two scale24 values. */
#define SP_SCALE24_MUL(_v1, _v2) unsigned((double)(_v1) * (_v2) / SP_SCALE24_MAX + .5)


/// 24 bit data type internal to SPStyle.
// Used only for opacity, fill-opacity, stroke-opacity.
// Opacity does not inherit but stroke-opacity and fill-opacity do. 
class SPIScale24 : public SPIBase
{

public:
    SPIScale24()
        : SPIBase( "anonymous_scale24" ),
          value(0)
    {}

    SPIScale24( Glib::ustring const &name, unsigned value = 0, bool inherits = true )
        : SPIBase( name, inherits ),
          value(value),
          value_default(value)
    {}

    ~SPIScale24() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        value = value_default;
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIScale24& operator=(const SPIScale24& rhs) = default;

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }


  // To do: make private
public:
    unsigned value : 24;

private:
    unsigned value_default : 24;
};


enum SPCSSUnit {
    SP_CSS_UNIT_NONE,
    SP_CSS_UNIT_PX,
    SP_CSS_UNIT_PT,
    SP_CSS_UNIT_PC,
    SP_CSS_UNIT_MM,
    SP_CSS_UNIT_CM,
    SP_CSS_UNIT_IN,
    SP_CSS_UNIT_EM,
    SP_CSS_UNIT_EX,
    SP_CSS_UNIT_PERCENT
};


/// Length type internal to SPStyle.
// Needs access to 'font-size' and 'font-family' for computed values.
// Used for 'stroke-width' 'stroke-dash-offset' ('none' not handled), text-indent
class SPILength : public SPIBase
{

public:
    SPILength()
        : SPIBase( "anonymous_length" ),
          unit(SP_CSS_UNIT_NONE),
          value(0),
          computed(0)
    {}

    SPILength( Glib::ustring const &name, float value = 0 )
        : SPIBase( name ),
          unit(SP_CSS_UNIT_NONE),
          value(value),
          computed(value),
          value_default(value)
    {}

    ~SPILength() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        unit = SP_CSS_UNIT_NONE, value = value_default;
        computed = value_default;
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPILength& operator=(const SPILength& rhs) = default;

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }
    void setDouble(double v);
    virtual const Glib::ustring toString(bool wname = false) const;

    // To do: make private
  public:
    unsigned unit : 4;
    float value;
    float computed;

private:
    float value_default;
};


/// Extended length type internal to SPStyle.
// Used for: line-height, letter-spacing, word-spacing
class SPILengthOrNormal : public SPILength
{

public:
    SPILengthOrNormal()
        : SPILength( "anonymous_length" ),
          normal(true)
    {}

    SPILengthOrNormal( Glib::ustring const &name, float value = 0 )
        : SPILength( name, value ),
          normal(true)
    {}

    ~SPILengthOrNormal() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPILength::clear();
        normal = true;
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPILengthOrNormal& operator=(const SPILengthOrNormal& rhs) = default;

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

  // To do: make private
public:
    bool normal : 1;
};


/// Extended length type internal to SPStyle.
// Used for: font-variation-settings
class SPIFontVariationSettings : public SPIBase
{

public:
    SPIFontVariationSettings()
        : SPIBase( "anonymous_fontvariationsettings" ),
          normal(true)
    {}

    SPIFontVariationSettings( Glib::ustring const &name )
        : SPIBase( name ),
          normal(true)
    {}

    ~SPIFontVariationSettings() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        axes.clear();
        normal = true;
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIFontVariationSettings& operator=(const SPIFontVariationSettings& rhs) {
        SPIBase::operator=(rhs);
        axes = rhs.axes;
        normal = rhs.normal;
        return *this;
    }

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

    virtual const Glib::ustring toString() const;

  // To do: make private
public:
    bool normal : 1;
    bool inherit : 1;
    std::map<Glib::ustring, float> axes;
};


/// Enum type internal to SPStyle.
// Used for many properties. 'font-stretch' and 'font-weight' must be special cased.
class SPIEnum : public SPIBase
{

public:
    SPIEnum() :
        SPIBase( "anonymous_enum" ),
        enums( nullptr ),
        value(0),
        computed(0)
    {}

    SPIEnum( Glib::ustring const &name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) :
        SPIBase( name, inherits ),
        enums( enums ),
        value(value),
        computed(value),
        value_default(value),
        computed_default(value)
    {}

    // Following is needed for font-weight
    SPIEnum( Glib::ustring const &name, SPStyleEnum const *enums, SPCSSFontWeight value, SPCSSFontWeight computed ) :
        SPIBase( name ),
        enums( enums ),
        value(value),
        computed(computed),
        value_default(value),
        computed_default(computed)
    {}

    ~SPIEnum() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        value = value_default, computed = computed_default;
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIEnum& operator=(const SPIEnum& rhs) {
        SPIBase::operator=(rhs);
        value            = rhs.value;
        computed         = rhs.computed;
        value_default    = rhs.value_default;
        computed_default = rhs.computed_default;
        return *this;
    }

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

  // To do: make private
public:
    SPStyleEnum const *enums;

    unsigned value : 16;  // 9 bits required for 'font-variant-east-asian'
    unsigned computed: 16;

private:
    unsigned value_default : 16;
    unsigned computed_default: 16; // for font-weight
};


/// SPIEnum w/ bits, allows values with multiple key words.
class SPIEnumBits : public SPIEnum
{

public:
    SPIEnumBits() :
        SPIEnum( "anonymous_enumbits", nullptr )
    {}

    SPIEnumBits( Glib::ustring const &name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) :
        SPIEnum( name, enums, value, inherits )
    {}

    ~SPIEnumBits() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
};


/// SPIEnum w/ extra bits. The 'font-variants-ligatures' property is a complete mess that needs
/// special handling. For OpenType fonts the values 'common-ligatures', 'contextual',
/// 'no-discretionary-ligatures', and 'no-historical-ligatures' are not useful but we still must be
/// able to parse them.
class SPILigatures : public SPIEnum
{

public:
    SPILigatures() :
        SPIEnum( "anonymous_enumligatures", nullptr )
    {}

    SPILigatures( Glib::ustring const &name, SPStyleEnum const *enums) :
        SPIEnum( name, enums, SP_CSS_FONT_VARIANT_LIGATURES_NORMAL )
    {}

    ~SPILigatures() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
};


/// SPIEnum w/ extra bits. The 'font-variants-numeric' property is a complete mess that needs
/// special handling. Multiple key words can be specified, some exclusive of others.
class SPINumeric : public SPIEnum
{

public:
    SPINumeric() :
        SPIEnum( "anonymous_enumnumeric", nullptr )
    {}

    SPINumeric( Glib::ustring const &name, SPStyleEnum const *enums) :
        SPIEnum( name, enums, SP_CSS_FONT_VARIANT_NUMERIC_NORMAL )
    {}

    ~SPINumeric() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
};


/// SPIEnum w/ extra bits. The 'font-variants-east-asian' property is a complete mess that needs
/// special handling. Multiple key words can be specified, some exclusive of others.
class SPIEastAsian : public SPIEnum
{

public:
    SPIEastAsian() :
        SPIEnum( "anonymous_enumeastasian", nullptr )
    {}

    SPIEastAsian( Glib::ustring const &name, SPStyleEnum const *enums) :
        SPIEnum( name, enums, SP_CSS_FONT_VARIANT_EAST_ASIAN_NORMAL )
    {}

    ~SPIEastAsian() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
};


/// String type internal to SPStyle.
// Used for 'marker', ..., 'font', 'font-family', 'inkscape-font-specification'
class SPIString : public SPIBase
{

public:
    SPIString()
        : SPIBase( "anonymous_string" ),
          value(nullptr)
    {}

    // TODO probably want to avoid gchar* and c-style strings.
    SPIString( Glib::ustring const &name, gchar const* value_default_in = nullptr )
        : SPIBase( name ),
          value(nullptr),
          value_default(value_default_in ? g_strdup(value_default_in) : nullptr)
    {}

    ~SPIString() override {
        g_free(value);
        g_free(value_default);
    }

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override; // TODO check about value and value_default
    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIString& operator=(const SPIString& rhs) {
        SPIBase::operator=(rhs);
        g_free(value);
        g_free(value_default);
        value            = rhs.value ? g_strdup(rhs.value) : nullptr;
        value_default    = rhs.value_default ? g_strdup(rhs.value_default) : nullptr;
        return *this;
    }

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

  // To do: make private, convert value to Glib::ustring
public:
    gchar *value;
    gchar *value_default;
};

/// Shapes type internal to SPStyle.
// Used for 'shape-inside', shape-subtract'
// Differs from SPIString by creating/deleting listeners on referenced shapes.
class SPIShapes : public SPIString
{

public:
    SPIShapes()
        : SPIString()
    {}

    // TODO probably want to avoid gchar* and c-style strings.
    SPIShapes( Glib::ustring const &name, gchar const* value_default_in = nullptr )
        : SPIString( name, value_default_in )
    {}

    void read( gchar const *str ) override;

public:
    std::vector<Glib::ustring> shape_ids;
};

/// Color type internal to SPStyle, FIXME Add string value to store SVG named color.
class SPIColor : public SPIBase
{

public:
    SPIColor()
        : SPIBase( "anonymous_color" ),
          currentcolor(false) {
        value.color.set(0);
    }

    SPIColor( Glib::ustring const &name, bool inherits = true )
        : SPIBase( name, inherits ),
          currentcolor(false) {
        value.color.set(0);
    }

    ~SPIColor() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        value.color.set(0);
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIColor& operator=(const SPIColor& rhs) {
        SPIBase::operator=(rhs);
        currentcolor = rhs.currentcolor;
        value.color  = rhs.value.color;
        return *this;
    }

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

    void setColor( float r, float g, float b ) {
        value.color.set( r, g, b );
    }

    void setColor( guint32 val ) {
        value.color.set( val );
    }

    void setColor( SPColor const& color ) {
        value.color = color;
    }

public:
    bool currentcolor : 1;
    // FIXME: remove structure and derive SPIPaint from this class.
    struct {
         SPColor color;
    } value;
};



#define SP_STYLE_FILL_SERVER(s) ((const_cast<SPStyle *> (s))->getFillPaintServer())
#define SP_STYLE_STROKE_SERVER(s) ((const_cast<SPStyle *> (s))->getStrokePaintServer())

// SVG 2
enum SPPaintOrigin {
    SP_CSS_PAINT_ORIGIN_NORMAL,
    SP_CSS_PAINT_ORIGIN_CURRENT_COLOR,
    SP_CSS_PAINT_ORIGIN_CONTEXT_FILL,
    SP_CSS_PAINT_ORIGIN_CONTEXT_STROKE
};


/// Paint type internal to SPStyle.
class SPIPaint : public SPIBase
{

public:
    SPIPaint()
        : SPIBase( "anonymous_paint" ),
          paintOrigin( SP_CSS_PAINT_ORIGIN_NORMAL ),
          colorSet(false),
          noneSet(false) {
        value.href = nullptr;
        clear();
    }

    SPIPaint( Glib::ustring const &name )
        : SPIBase( name ),
          colorSet(false),
          noneSet(false) {
        value.href = nullptr;
        clear();  // Sets defaults
    }

    ~SPIPaint() override;  // Clear and delete href.
    void read( gchar const *str ) override;
    virtual void read( gchar const *str, SPStyle &style, SPDocument *document = nullptr);
    const Glib::ustring get_value() const override;
    void clear() override;
    virtual void reset( bool init ); // Used internally when reading or cascading
    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIPaint& operator=(const SPIPaint& rhs) {
        SPIBase::operator=(rhs);
        paintOrigin     = rhs.paintOrigin;
        colorSet        = rhs.colorSet;
        noneSet         = rhs.noneSet;
        value.color     = rhs.value.color;
        value.href      = rhs.value.href;
        return *this;
    }

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

    bool isSameType( SPIPaint const & other ) const {
        return (isPaintserver() == other.isPaintserver()) && (colorSet == other.colorSet) && (paintOrigin == other.paintOrigin);
    }

    bool isNoneSet() const {
        return noneSet;
    }

    bool isNone() const {
        return !colorSet && !isPaintserver() && (paintOrigin == SP_CSS_PAINT_ORIGIN_NORMAL);
    } // TODO refine

    bool isColor() const {
        return colorSet && !isPaintserver();
    }

    bool isPaintserver() const {
        return value.href && value.href->getObject() != nullptr;
    }

    void setColor( float r, float g, float b ) {
        value.color.set( r, g, b ); colorSet = true;
    }

    void setColor( guint32 val ) {
        value.color.set( val ); colorSet = true;
    }

    void setColor( SPColor const& color ) {
        value.color = color; colorSet = true;
    }

    void setNone() {noneSet = true; colorSet=false;}

  // To do: make private
public:
    SPPaintOrigin paintOrigin : 2;
    bool colorSet : 1;
    bool noneSet : 1;
    struct {
         SPPaintServerReference *href;
         SPColor color;
    } value;
};


// SVG 2
enum SPPaintOrderLayer {
    SP_CSS_PAINT_ORDER_NORMAL,
    SP_CSS_PAINT_ORDER_FILL,
    SP_CSS_PAINT_ORDER_STROKE,
    SP_CSS_PAINT_ORDER_MARKER
};

// Normal maybe should be moved out as is done in other classes.
// This could be replaced by a generic enum class where multiple keywords are allowed and
// where order matters (in contrast to 'text-decoration-line' where order does not matter).

// Each layer represents a layer of paint which can be a fill, a stroke, or markers.
const size_t PAINT_ORDER_LAYERS = 3;

/// Paint order type internal to SPStyle
class SPIPaintOrder : public SPIBase
{

public:
    SPIPaintOrder()
        : SPIBase( "paint-order" ),
          value(nullptr) {
        this->clear();
    }

    ~SPIPaintOrder() override {
        g_free( value );
    }

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        for( unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i ) {
            layer[i]     = SP_CSS_PAINT_ORDER_NORMAL;
            layer_set[i] = false;
        }
        g_free(value);
        value = nullptr;
    }
    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIPaintOrder& operator=(const SPIPaintOrder& rhs) {
        SPIBase::operator=(rhs);
        for( unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i ) {
            layer[i]     = rhs.layer[i];
            layer_set[i] = rhs.layer_set[i];
        }
        g_free(value);
        value            = g_strdup(rhs.value);
        return *this;
    }

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }


  // To do: make private
public:
    SPPaintOrderLayer layer[PAINT_ORDER_LAYERS];
    bool layer_set[PAINT_ORDER_LAYERS];
    gchar *value;  // Raw string
};


/// Filter type internal to SPStyle
class SPIDashArray : public SPIBase
{

public:
    SPIDashArray()
        : SPIBase( "stroke-dasharray" )
    {}  // Only one instance of SPIDashArray

    ~SPIDashArray() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        values.clear();
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIDashArray& operator=(const SPIDashArray& rhs) = default;

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

  // To do: make private, change double to SVGLength
public:
  std::vector<SPILength> values;
};

/// Filter type internal to SPStyle
class SPIFilter : public SPIBase
{

public:
    SPIFilter()
        : SPIBase( "filter", false ),
          href(nullptr)
    {}

    ~SPIFilter() override;
    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override;
    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIFilter& operator=(const SPIFilter& rhs) = default;

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

  // To do: make private
public:
    SPFilterReference *href;
};



enum {
    SP_FONT_SIZE_LITERAL,
    SP_FONT_SIZE_LENGTH,
    SP_FONT_SIZE_PERCENTAGE
};

/// Fontsize type internal to SPStyle (also used by libnrtype/Layout-TNG-Input.cpp).
class SPIFontSize : public SPIBase
{

public:
    SPIFontSize()
        : SPIBase( "font-size" ) {
        this->clear();
    }

    ~SPIFontSize() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        type = SP_FONT_SIZE_LITERAL, unit = SP_CSS_UNIT_NONE,
            literal = SP_CSS_FONT_SIZE_MEDIUM, value = 12.0, computed = 12.0;
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIFontSize& operator=(const SPIFontSize& rhs) = default;

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

public:
    static float const font_size_default;

  // To do: make private
public:
    unsigned type : 2;
    unsigned unit : 4;
    unsigned literal : 4;
    float value;
    float computed;

private:
    double relative_fraction() const;
    static float const font_size_table[];
};


/// Font type internal to SPStyle ('font' shorthand)
class SPIFont : public SPIBase
{

public:
    SPIFont()
        : SPIBase( "font" )
    {}

    ~SPIFont() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
    }

    void cascade( const SPIBase* const /*parent*/ ) override
    {} // Done in dependent properties

    void merge(   const SPIBase* const /*parent*/ ) override
    {}

    SPIFont& operator=(const SPIFont& rhs) = default;

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }
};


enum {
    SP_BASELINE_SHIFT_LITERAL,
    SP_BASELINE_SHIFT_LENGTH,
    SP_BASELINE_SHIFT_PERCENTAGE
};

/// Baseline shift type internal to SPStyle. (This is actually just like SPIFontSize)
class SPIBaselineShift : public SPIBase
{

public:
    SPIBaselineShift()
        : SPIBase( "baseline-shift", false ) {
        this->clear();
    }

    ~SPIBaselineShift() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        type=SP_BASELINE_SHIFT_LITERAL, unit=SP_CSS_UNIT_NONE,
            literal = SP_CSS_BASELINE_SHIFT_BASELINE, value = 0.0, computed = 0.0;
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPIBaselineShift& operator=(const SPIBaselineShift& rhs) = default;

    // This is not used but we have it for completeness, it has not been tested.
    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

    bool isZero() const;

  // To do: make private
public:
    unsigned type : 2;
    unsigned unit : 4;
    unsigned literal: 2;
    float value; // Can be negative
    float computed;
};

// CSS 2.  Changes in CSS 3, where description is for TextDecorationLine, NOT TextDecoration
// See http://www.w3.org/TR/css-text-decor-3/

// CSS3 2.2
/// Text decoration line type internal to SPStyle.  THIS SHOULD BE A GENERIC CLASS
class SPITextDecorationLine : public SPIBase
{

public:
    SPITextDecorationLine()
        : SPIBase( "text-decoration-line" ) {
        this->clear();
    }

    ~SPITextDecorationLine() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        underline = false, overline = false, line_through = false, blink = false;
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPITextDecorationLine& operator=(const SPITextDecorationLine& rhs) = default;

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

  // To do: make private
public:
    bool underline : 1;
    bool overline : 1;
    bool line_through : 1;
    bool blink : 1;    // "Conforming user agents are not required to support this value." yay!
};

// CSS3 2.2
/// Text decoration style type internal to SPStyle.  THIS SHOULD JUST BE SPIEnum!
class SPITextDecorationStyle : public SPIBase
{

public:
    SPITextDecorationStyle()
        : SPIBase( "text-decoration-style" ) {
        this->clear();
    }

    ~SPITextDecorationStyle() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        solid = true, isdouble = false, dotted = false, dashed = false, wavy = false;
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPITextDecorationStyle& operator=(const SPITextDecorationStyle& rhs) = default;

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

  // To do: make private
public:
    bool solid : 1;
    bool isdouble : 1;  // cannot use "double" as it is a reserved keyword
    bool dotted : 1;
    bool dashed : 1;
    bool wavy : 1;
};



// This class reads in both CSS2 and CSS3 'text-decoration' property. It passes the line, style,
// and color parts to the appropriate CSS3 long-hand classes for reading and storing values.  When
// writing out data, we write all four properties, with 'text-decoration' being written out with
// the CSS2 format. This allows CSS1/CSS2 renderers to at least render lines, even if they are not
// the right style. (See http://www.w3.org/TR/css-text-decor-3/#text-decoration-property )

/// Text decoration type internal to SPStyle.
class SPITextDecoration : public SPIBase
{

public:
    SPITextDecoration()
        : SPIBase( "text-decoration" ),
          style_td( nullptr )
    {}

    ~SPITextDecoration() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
                                       SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
                                       SPIBase const *const base = nullptr ) const override;
    void clear() override {
        SPIBase::clear();
        style_td = nullptr;
    }

    void cascade( const SPIBase* const parent ) override;
    void merge(   const SPIBase* const parent ) override;

    SPITextDecoration& operator=(const SPITextDecoration& rhs) {
        SPIBase::operator=(rhs);
        return *this;
    }

    // Use CSS2 value
    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

public:
    SPStyle* style_td;   // Style to be used for drawing CSS2 text decorations 
};


// These are used to implement text_decoration. The values are not saved to or read from SVG file
struct SPITextDecorationData {
    float   phase_length;          // length along text line,used for phase for dot/dash/wavy
    bool    tspan_line_start;      // is first  span on a line
    bool    tspan_line_end;        // is last span on a line
    float   tspan_width;           // from libnrtype, when it calculates spans
    float   ascender;              // the rest from tspan's font
    float   descender;
    float   underline_thickness;
    float   underline_position; 
    float   line_through_thickness;
    float   line_through_position;
};

/// Vector Effects.  THIS SHOULD BE A GENERIC CLASS
class SPIVectorEffect : public SPIBase
{

public:
    SPIVectorEffect()
        : SPIBase( "vector-effect" ) {
        this->clear();
        inherits = false;
    }

    ~SPIVectorEffect() override
    = default;

    void read( gchar const *str ) override;
    const Glib::ustring get_value() const override;
    void clear() override {
        SPIBase::clear();
        stroke = false;
        size   = false;
        rotate = false;
        fixed  = false;
    }

    // Does not inherit
    void cascade( const SPIBase* const parent ) override {};
    void merge(   const SPIBase* const parent ) override {};

    SPIVectorEffect& operator=(const SPIVectorEffect& rhs) = default;

    bool operator==(const SPIBase& rhs) override;
    bool operator!=(const SPIBase& rhs) override {
        return !(*this == rhs);
    }

  // To do: make private
public:
    bool stroke : 1;
    bool size   : 1;
    bool rotate : 1;
    bool fixed  : 1;
};

#endif // SEEN_SP_STYLE_INTERNAL_H


/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
