/*
 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
 * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * 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 02110-1301, USA.
 */

#ifndef SVGAnimatedPropertyMacros_h
#define SVGAnimatedPropertyMacros_h

#include "Element.h"
#include "SVGAnimatedProperty.h"
#include "SVGAttributeToPropertyMap.h"
#include "SVGPropertyTraits.h"
#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>

namespace WebCore {

// SVGSynchronizableAnimatedProperty implementation
template<typename PropertyType>
struct SVGSynchronizableAnimatedProperty {
    SVGSynchronizableAnimatedProperty()
        : value(SVGPropertyTraits<PropertyType>::initialValue())
        , shouldSynchronize(false)
        , isValid(false)
    {
    }

    template<typename ConstructorParameter1>
    SVGSynchronizableAnimatedProperty(const ConstructorParameter1& value1)
        : value(value1)
        , shouldSynchronize(false)
        , isValid(false)
    {
    }

    template<typename ConstructorParameter1, typename ConstructorParameter2>
    SVGSynchronizableAnimatedProperty(const ConstructorParameter1& value1, const ConstructorParameter2& value2)
        : value(value1, value2)
        , shouldSynchronize(false)
        , isValid(false)
    {
    }

    void synchronize(Element* ownerElement, const QualifiedName& attrName, const AtomicString& value)
    {
        ownerElement->setSynchronizedLazyAttribute(attrName, value);
    }

    PropertyType value;
    bool shouldSynchronize : 1;
    bool isValid : 1;
};

// Property registration helpers
#define BEGIN_REGISTER_ANIMATED_PROPERTIES(OwnerType) \
SVGAttributeToPropertyMap& OwnerType::attributeToPropertyMap() \
{ \
    static NeverDestroyed<SVGAttributeToPropertyMap> map; \
    return map; \
} \
\
static void registerAnimatedPropertiesFor##OwnerType() \
{ \
    auto& map = OwnerType::attributeToPropertyMap(); \
    if (!map.isEmpty()) \
        return; \
    typedef OwnerType UseOwnerType;

#define REGISTER_LOCAL_ANIMATED_PROPERTY(LowerProperty) map.addProperty(*UseOwnerType::LowerProperty##PropertyInfo());
#define REGISTER_PARENT_ANIMATED_PROPERTIES(ClassName) map.addProperties(ClassName::attributeToPropertyMap());
#define END_REGISTER_ANIMATED_PROPERTIES }

// Property definition helpers (used in SVG*.cpp files)
#define DEFINE_ANIMATED_PROPERTY(AnimatedPropertyTypeEnum, OwnerType, DOMAttribute, SVGDOMAttributeIdentifier, UpperProperty, LowerProperty) \
const SVGPropertyInfo* OwnerType::LowerProperty##PropertyInfo() { \
    static NeverDestroyed<const SVGPropertyInfo> s_propertyInfo = SVGPropertyInfo \
                        (AnimatedPropertyTypeEnum, \
                         PropertyIsReadWrite, \
                         DOMAttribute, \
                         SVGDOMAttributeIdentifier, \
                         &OwnerType::synchronize##UpperProperty, \
                         &OwnerType::lookupOrCreate##UpperProperty##Wrapper); \
    return &s_propertyInfo.get(); \
} 

// Property declaration helpers (used in SVG*.h files)
#define BEGIN_DECLARE_ANIMATED_PROPERTIES_BASE(OwnerType) \
public: \
    static SVGAttributeToPropertyMap& attributeToPropertyMap(); \
    virtual SVGAttributeToPropertyMap& localAttributeToPropertyMap() \
    { \
        return attributeToPropertyMap(); \
    } \
    typedef OwnerType UseOwnerType;

#define BEGIN_DECLARE_ANIMATED_PROPERTIES(OwnerType) \
public: \
    static SVGAttributeToPropertyMap& attributeToPropertyMap(); \
    SVGAttributeToPropertyMap& localAttributeToPropertyMap() override \
    { \
        return attributeToPropertyMap(); \
    } \
    typedef OwnerType UseOwnerType;

#define DECLARE_ANIMATED_PROPERTY(TearOffType, PropertyType, UpperProperty, LowerProperty, OverrideSpecifier) \
public: \
    static const SVGPropertyInfo* LowerProperty##PropertyInfo(); \
    PropertyType& LowerProperty() const \
    { \
        if (auto wrapper = SVGAnimatedProperty::lookupWrapper<UseOwnerType, TearOffType>(this, LowerProperty##PropertyInfo())) { \
            if (wrapper->isAnimating()) \
                return wrapper->currentAnimatedValue(); \
        } \
        return m_##LowerProperty.value; \
    } \
\
    PropertyType& LowerProperty##BaseValue() const OverrideSpecifier \
    { \
        return m_##LowerProperty.value; \
    } \
\
    void set##UpperProperty##BaseValue(const PropertyType& type, const bool validValue = true) OverrideSpecifier \
    { \
        m_##LowerProperty.value = type; \
        m_##LowerProperty.isValid = validValue; \
    } \
\
    Ref<TearOffType> LowerProperty##Animated() \
    { \
        m_##LowerProperty.shouldSynchronize = true; \
        return static_reference_cast<TearOffType>(lookupOrCreate##UpperProperty##Wrapper(this)); \
    } \
\
    bool LowerProperty##IsValid() const \
    { \
        return m_##LowerProperty.isValid; \
    } \
\
private: \
    void synchronize##UpperProperty() \
    { \
        if (!m_##LowerProperty.shouldSynchronize) \
            return; \
        AtomicString value(SVGPropertyTraits<PropertyType>::toString(m_##LowerProperty.value)); \
        m_##LowerProperty.synchronize(this, LowerProperty##PropertyInfo()->attributeName, value); \
    } \
\
    static Ref<SVGAnimatedProperty> lookupOrCreate##UpperProperty##Wrapper(SVGElement* maskedOwnerType) \
    { \
        ASSERT(maskedOwnerType); \
        UseOwnerType* ownerType = static_cast<UseOwnerType*>(maskedOwnerType); \
        return SVGAnimatedProperty::lookupOrCreateWrapper<UseOwnerType, TearOffType, PropertyType>(ownerType, LowerProperty##PropertyInfo(), ownerType->m_##LowerProperty.value); \
    } \
\
    static void synchronize##UpperProperty(SVGElement* maskedOwnerType) \
    { \
        ASSERT(maskedOwnerType); \
        UseOwnerType* ownerType = static_cast<UseOwnerType*>(maskedOwnerType); \
        ownerType->synchronize##UpperProperty(); \
    } \
\
    mutable SVGSynchronizableAnimatedProperty<PropertyType> m_##LowerProperty;

#define END_DECLARE_ANIMATED_PROPERTIES 

// List specific definition/declaration helpers
#define DECLARE_ANIMATED_LIST_PROPERTY(TearOffType, PropertyType, UpperProperty, LowerProperty) \
DECLARE_ANIMATED_PROPERTY(TearOffType, PropertyType, UpperProperty, LowerProperty, ) \
void detachAnimated##UpperProperty##ListWrappers(unsigned newListSize) \
{ \
    if (auto wrapper = SVGAnimatedProperty::lookupWrapper<UseOwnerType, TearOffType>(this, LowerProperty##PropertyInfo())) \
        wrapper->detachListWrappers(newListSize); \
}

}

#endif // SVGAnimatedPropertyMacros_h
