// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef __INK_SVG_PROFILE_H__
#define __INK_SVG_PROFILE_H__
/*
 * A class for managing which SVG Profiles are used.
 *
 * Authors:
 *   Ted Gould <ted@gould.cx>
 *
 * Copyright (C) 2004 Authors
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

namespace Inkscape {

/**
    \brief  A class that contains information on which SVG profiles are
            marked for the object.

    The basic functionality here is to allow marking of which profiles
    are supported or required by some other object.  The basic
    implementation of that is a bitfield, a data type that has bits
    to be able to be set depending on whether or not that particular
    feature is available.  This implementation is a little more complex
    for a coulple of reasons.

    The first reason for making it be more complex is to make it into
    a nice C++ friendly class, where the actual bits aren't required
    by the calling application.  This is accomplished by use of the
    \c SvgProfileType enum, and having the various fields in there.

    The second reason that this is more complex is that it is reasonable
    that the enum will grow to be greater than the native integer of
    a given system.  As more profiles and distinctions are made, there
    will have to be more entries in the enum.  In order to combat this,
    the \c BitField class was created.  This creates consistent operations
    independent of how many integers are used to represent the particular
    bitfield.
 
    The entire class has been optimized for inlining and compiler reduction
    of code.  All entries should allow being put in a structure or other
    static allocation.  For most operations, simple and/or instructions
    in the processor is required after optimization.

    Adding additional profiles to the class is as easy as adding entries
    into the enum.  Adding additional aggregates requires adding to the
    enum, and adding the aggregate into the constructor for the class.
*/
class SvgProfile {
public:
    /** \brief  The enum listing all the different profiles supported, and
                some aggregate ones supported.

        \note  This \c enum should not be used for anything other than passing
               data into the constructor of \c SvgProfile.  The optimization
               of the constructor is best when it is passed a static value.
               So, passing this enum through a function, and then constructing
               the \c SvgProfile would be less than ideal.  Plus, as far as
               memory goes, today, they take up the same amount of memory.
    */
    enum SvgProfileType {
        BASIC_OPERATION = 0,  /**< This describes a feature that is part
                                   of the basic functionality of Inkscape
                                   itself.  This would be like save or open. */
        SVG_BASE_1_0,         /**< The base SVG spec version 1.0 */
        SVG_BASE_1_1,         /**< The base SVG spec version 1.1 */
        SVG_BASE_1_2,         /**< The base SVG spec version 1.2 */
        SVG_BASE_2_0,         /**< The base SVG spec version 2.0 */

        SVG_MOBILE_TINY_1_1,  /**< The mobile SVG spec Tiny profile version 1.1 */
        SVG_MOBILE_BASIC_1_1, /**< The mobile SVG spec Basic profile version 1.1 */
        SVG_MOBILE_TINY_1_2,  /**< The mobile SVG spec Tiny profile version 1.2 */
        SVG_MOBILE_BASIC_1_2, /**< The mobile SVG spec Basic profile version 1.2 */

        SVG_PRINT_1_1,        /**< */

        PROFILE_UNIQUE_CNT,   /**< A marker to separate between the entries
                                   that are unique, and those which are
                                   aggregates of them. */

        SVG_BASE_ALL,         /**< Every version of the SVG Base spec */
        SVG_MOBILE_ALL,       /**< Every version and profile in SVG Mobile */
        SVG_TINY_ALL,         /**< Every version of the tiny profile in SVG Mobile */
        SVG_BASIC_ALL,        /**< Every version of the basic profile in SVG Mobile */
        ALL                   /**< All, everything, basically doesn't tell you anything at all */
    };

private:
    /** \brief  The core of the \c SvgProfile class, this implements a
                bitfield which can be several integers large with standard
                operations independent of size. */
    class BitField {
        /** \brief A quick way identify the number of bits in an integer. */
        #define  BITS_IN_INT (sizeof(int) * 8)
        /** \brief The size of the array which is being used. */
        #define  ARRAY_SIZE  (((PROFILE_UNIQUE_CNT - 1) / BITS_IN_INT) + 1)

            /** \brief The actual array holding the bitfield. */
            unsigned int bits[ARRAY_SIZE];

        public:
            /** \brief Constructor for the bitfield, it clears the \c bits
                       array by setting things to zero. */
            inline BitField(void) {
                for (int i = 0; i < ARRAY_SIZE; i++) {
                    bits[i] = 0;
                }
            }

            /** \brief Constructs a bitfield by passing in another array
                       describing how the bits should look.  The function
                       just copies the array into \c bits. */
            inline BitField(unsigned int in_bits[ARRAY_SIZE]) {
                for (int i = 0; i < ARRAY_SIZE; i++) {
                    bits[i] = in_bits[i];
                }
            }

            /** \brief The equals operator, but it doesn't do quite that.
                       This function checks to see if there are bits that
                       are similarly high in both bitfields. */
            inline bool operator == (const BitField &in_field) const {
                for (int i = 0; i < ARRAY_SIZE; i++) {
                    if (bits[i] & in_field.bits[i] != 0)
                        return true;
                }
                return false;
            }

            /** \brief A convenience function to set a particular bit in the
                       bitfield

                This function first find which integer the bit is in by
                dividing by \c BITS_IN_INT and then which bit in the
                integer by getting the modulus.  The selected integer is
                the \c |= with a \c 1 shifted left by the position.
            */
            inline void set (const unsigned int pos) {
                unsigned int array_pos = pos / BITS_IN_INT;
                unsigned int bit_pos   = pos % BITS_IN_INT;
                bits[array_pos] |= 1 << bit_pos;
            }

            /** \brief Does a bitwise \c OR on two bitfields.  It does
                       this for the entire \c bits array. */
            inline BitField operator | (const BitField &other) const {
                unsigned int local_bits[ARRAY_SIZE];

                for (int i = 0; i < ARRAY_SIZE; i++) {
                    local_bits[i] = bits[i] | other.bits[i];
                }

                return BitField(local_bits);
            }

            /** \brief  Causes one \c BitField to take on the values
                        stored in a different bitfield. */
            inline BitField & operator = (const BitField &other) {
                for (int i = 0; i < ARRAY_SIZE; i++) {
                    bits[i] = other.bits[i];
                }

                return *this;
            }

            /** \brief  Does a logical \c OR of the bitfield with another
                        bitfield.  It does this by using \c |=. */
            inline BitField & operator |= (const BitField &other) {
                for (int i = 0; i < ARRAY_SIZE; i++) {
                    bits[i] |= other.bits[i];
                }
                return *this;    
            }

        #undef BITS_IN_INT
        #undef ARRAY_SIZE
    };
    
    /** \brief The actual data stored on the profile. */
    BitField _profile;

    /**
        \brief  Create an SvgProfile with an already created bitfield
        \param  in_field  The bitfield that should be used in the profile

        This function just copies the incoming bitfield into the one
        which is allocated for this.
    */
    inline SvgProfile (const BitField &in_field) {
        _profile = in_field;
    }

public:
    /** \brief A constructor for \c SvgProfile which sets up the bitfield
               based on the \c SvgProfileType getting passed in.

        This function has basically two different modes of operation
        depending on whether the requested value is a pure profile or
        and aggregate.  If it is pure, then that bit is set in \c _profile
        and the function exits.  Otherwise a case statement is used to
        determine which aggregate is called, and then setting all of
        the bits for that aggregate.
    */
    inline SvgProfile (SvgProfileType type) {
        if (type < PROFILE_UNIQUE_CNT) {
            _profile.set(type);
        } else {
            /* Okay, so this could be done by OR'ing a bunch of these
               together, but I thought that would reduce the chance of
               the compiler actually figuring it all out and optimizing
               everything.  This is already getting pretty complex. */
            switch (type) {
                case SVG_BASE_ALL:
                    _profile.set(SVG_BASE_1_0);
                    _profile.set(SVG_BASE_1_1);
                    _profile.set(SVG_BASE_1_2);
                    _profile.set(SVG_BASE_2_0);
                    break;
                case SVG_BASIC_ALL:
                    _profile.set(SVG_MOBILE_BASIC_1_1);
                    _profile.set(SVG_MOBILE_BASIC_1_2);
                    break;
                case SVG_TINY_ALL:
                    _profile.set(SVG_MOBILE_TINY_1_1);
                    _profile.set(SVG_MOBILE_TINY_1_2);
                    break;
                case SVG_MOBILE_ALL:
                    _profile.set(SVG_MOBILE_BASIC_1_1);
                    _profile.set(SVG_MOBILE_BASIC_1_2);
                    _profile.set(SVG_MOBILE_TINY_1_1);
                    _profile.set(SVG_MOBILE_TINY_1_2);
                    break;
                case ALL:
                    _profile.set(SVG_BASE_1_0);
                    _profile.set(SVG_BASE_1_1);
                    _profile.set(SVG_BASE_1_2);
                    _profile.set(SVG_BASE_2_0);

                    _profile.set(SVG_MOBILE_BASIC_1_1);
                    _profile.set(SVG_MOBILE_BASIC_1_2);

                    _profile.set(SVG_MOBILE_TINY_1_1);
                    _profile.set(SVG_MOBILE_TINY_1_2);
                    break;
            };
        }
        return;    
    };

    /** \brief A function to check equality of two \c SvgProfiles.
               It doesn't quite check equality though, it more ensures
               that there is a profile supported by both objects.  This
               would be similar to: (a & b) != 0 in a standard bitfield
               impelemtation.  But is done this way for simplicity. */
    inline bool operator == (const SvgProfile &in_profile) const {
        return _profile == in_profile._profile;
    };

    /** \brief A function allow combining of \c SvgProfiles with each
               other into a larger \c SvgProfile. */
    inline SvgProfile operator | (const SvgProfile &other) const {
        return SvgProfile(_profile | other._profile);
    }

    /** \brief A quick way to add additional profiles to the currently
               allocated object. */
    inline SvgProfile & operator |= (const SvgProfile &other) {
        _profile |= other._profile;
        return *this;
    }
};


}  /* namespace Inkscape */

#endif /* __INK_SVG_PROFILE_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 :
