/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsAttrValueInlines_h__
#define nsAttrValueInlines_h__

#include <stdint.h>

#include "nsAttrValue.h"
#include "mozilla/Attributes.h"

struct MiscContainer;

struct MiscContainer final
{
  typedef nsAttrValue::ValueType ValueType;

  ValueType mType;
  // mStringBits points to either nsIAtom* or nsStringBuffer* and is used when
  // mType isn't eGeckoCSSDeclaration.
  // Note eStringBase and eAtomBase is used also to handle the type of
  // mStringBits.
  uintptr_t mStringBits;
  union {
    struct {
      union {
        int32_t mInteger;
        nscolor mColor;
        uint32_t mEnumValue;
        int32_t mPercent;
        mozilla::css::Declaration* mGeckoCSSDeclaration;
        ServoDeclarationBlock* mServoCSSDeclaration;
        mozilla::css::URLValue* mURL;
        mozilla::css::ImageValue* mImage;
        nsAttrValue::AtomArray* mAtomArray;
        nsIntMargin* mIntMargin;
        const nsSVGAngle* mSVGAngle;
        const nsSVGIntegerPair* mSVGIntegerPair;
        const nsSVGLength2* mSVGLength;
        const mozilla::SVGLengthList* mSVGLengthList;
        const mozilla::SVGNumberList* mSVGNumberList;
        const nsSVGNumberPair* mSVGNumberPair;
        const mozilla::SVGPathData* mSVGPathData;
        const mozilla::SVGPointList* mSVGPointList;
        const mozilla::SVGAnimatedPreserveAspectRatio* mSVGPreserveAspectRatio;
        const mozilla::SVGStringList* mSVGStringList;
        const mozilla::SVGTransformList* mSVGTransformList;
        const nsSVGViewBox* mSVGViewBox;
      };
      uint32_t mRefCount : 31;
      uint32_t mCached : 1;
    } mValue;
    double mDoubleValue;
  };

  MiscContainer()
    : mType(nsAttrValue::eColor),
      mStringBits(0)
  {
    MOZ_COUNT_CTOR(MiscContainer);
    mValue.mColor = 0;
    mValue.mRefCount = 0;
    mValue.mCached = 0;
  }

protected:
  // Only nsAttrValue should be able to delete us.
  friend class nsAttrValue;

  ~MiscContainer()
  {
    if (IsRefCounted()) {
      MOZ_ASSERT(mValue.mRefCount == 0);
      MOZ_ASSERT(!mValue.mCached);
    }
    MOZ_COUNT_DTOR(MiscContainer);
  }

public:
  bool GetString(nsAString& aString) const;

  inline bool IsRefCounted() const
  {
    // Nothing stops us from refcounting (and sharing) other types of
    // MiscContainer (except eDoubleValue types) but there's no compelling
    // reason to.
    return mType == nsAttrValue::eGeckoCSSDeclaration ||
           mType == nsAttrValue::eServoCSSDeclaration;
  }

  inline int32_t AddRef() {
    MOZ_ASSERT(IsRefCounted());
    return ++mValue.mRefCount;
  }

  inline int32_t Release() {
    MOZ_ASSERT(IsRefCounted());
    return --mValue.mRefCount;
  }

  void Cache();
  void Evict();
};

/**
 * Implementation of inline methods
 */

inline int32_t
nsAttrValue::GetIntegerValue() const
{
  NS_PRECONDITION(Type() == eInteger, "wrong type");
  return (BaseType() == eIntegerBase)
         ? GetIntInternal()
         : GetMiscContainer()->mValue.mInteger;
}

inline int16_t
nsAttrValue::GetEnumValue() const
{
  NS_PRECONDITION(Type() == eEnum, "wrong type");
  // We don't need to worry about sign extension here since we're
  // returning an int16_t which will cut away the top bits.
  return static_cast<int16_t>((
    (BaseType() == eIntegerBase)
    ? static_cast<uint32_t>(GetIntInternal())
    : GetMiscContainer()->mValue.mEnumValue)
      >> NS_ATTRVALUE_ENUMTABLEINDEX_BITS);
}

inline float
nsAttrValue::GetPercentValue() const
{
  NS_PRECONDITION(Type() == ePercent, "wrong type");
  return ((BaseType() == eIntegerBase)
          ? GetIntInternal()
          : GetMiscContainer()->mValue.mPercent)
            / 100.0f;
}

inline nsAttrValue::AtomArray*
nsAttrValue::GetAtomArrayValue() const
{
  NS_PRECONDITION(Type() == eAtomArray, "wrong type");
  return GetMiscContainer()->mValue.mAtomArray;
}

inline mozilla::css::Declaration*
nsAttrValue::GetGeckoCSSDeclarationValue() const
{
  NS_PRECONDITION(Type() == eGeckoCSSDeclaration, "wrong type");
  return GetMiscContainer()->mValue.mGeckoCSSDeclaration;
}

inline ServoDeclarationBlock*
nsAttrValue::GetServoCSSDeclarationValue() const
{
  NS_PRECONDITION(Type() == eServoCSSDeclaration, "wrong type");
  return GetMiscContainer()->mValue.mServoCSSDeclaration;
}

inline mozilla::css::URLValue*
nsAttrValue::GetURLValue() const
{
  NS_PRECONDITION(Type() == eURL, "wrong type");
  return GetMiscContainer()->mValue.mURL;
}

inline mozilla::css::ImageValue*
nsAttrValue::GetImageValue() const
{
  NS_PRECONDITION(Type() == eImage, "wrong type");
  return GetMiscContainer()->mValue.mImage;
}

inline double
nsAttrValue::GetDoubleValue() const
{
  NS_PRECONDITION(Type() == eDoubleValue, "wrong type");
  return GetMiscContainer()->mDoubleValue;
}

inline bool
nsAttrValue::GetIntMarginValue(nsIntMargin& aMargin) const
{
  NS_PRECONDITION(Type() == eIntMarginValue, "wrong type");
  nsIntMargin* m = GetMiscContainer()->mValue.mIntMargin;
  if (!m)
    return false;
  aMargin = *m;
  return true;
}

inline bool
nsAttrValue::IsSVGType(ValueType aType) const
{
  return aType >= eSVGTypesBegin && aType <= eSVGTypesEnd;
}

inline bool
nsAttrValue::StoresOwnData() const
{
  if (BaseType() != eOtherBase) {
    return true;
  }
  ValueType t = Type();
  return t != eGeckoCSSDeclaration &&
         t != eServoCSSDeclaration &&
         !IsSVGType(t);
}

inline void
nsAttrValue::SetPtrValueAndType(void* aValue, ValueBaseType aType)
{
  NS_ASSERTION(!(NS_PTR_TO_INT32(aValue) & ~NS_ATTRVALUE_POINTERVALUE_MASK),
               "pointer not properly aligned, this will crash");
  mBits = reinterpret_cast<intptr_t>(aValue) | aType;
}

inline void
nsAttrValue::ResetIfSet()
{
  if (mBits) {
    Reset();
  }
}

inline MiscContainer*
nsAttrValue::GetMiscContainer() const
{
  NS_ASSERTION(BaseType() == eOtherBase, "wrong type");
  return static_cast<MiscContainer*>(GetPtr());
}

inline int32_t
nsAttrValue::GetIntInternal() const
{
  NS_ASSERTION(BaseType() == eIntegerBase,
               "getting integer from non-integer");
  // Make sure we get a signed value.
  // Lets hope the optimizer optimizes this into a shift. Unfortunatly signed
  // bitshift right is implementaion dependant.
  return static_cast<int32_t>(mBits & ~NS_ATTRVALUE_INTEGERTYPE_MASK) /
         NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER;
}

#endif
