#ifndef mozilla_dom_UnionTypes_h__
#define mozilla_dom_UnionTypes_h__

#include "mozilla/dom/BindingUtils.h"

namespace mozilla {
namespace dom {
class CanvasGradient;
class CanvasPattern;
class HTMLCanvasElement;
class HTMLImageElement;
class HTMLOptGroupElement;
class HTMLOptionElement;
class HTMLVideoElement;
} // namespace dom
} // namespace mozilla
class nsDOMEvent;
class nsGenericHTMLElement;

namespace mozilla {
namespace dom {
class EventOrString
{
  friend class EventOrStringArgument;
  enum Type
  {
    eUninitialized,
    eEvent,
    eString
  };
  union Value
  {
    UnionMember<NonNull<nsDOMEvent> > mEvent;
    UnionMember<NonNull<nsAString> > mString;
  };
  Type mType;
  Value mValue;

  EventOrString(const EventOrString&) MOZ_DELETE;
  void operator=(const EventOrString) MOZ_DELETE;

public:
  explicit inline EventOrString()
    : mType(eUninitialized)
  {
  }

  inline ~EventOrString()
  {
    switch (mType) {
      case eUninitialized: {
        break;
      }
      case eEvent: {
        DestroyEvent();
        break;
      }
      case eString: {
        DestroyString();
        break;
      }
    }
  }

  inline NonNull<nsDOMEvent>&
  SetAsEvent()
  {
    mType = eEvent;
    return mValue.mEvent.SetValue();
  }

  inline bool
  IsEvent() const
  {
    return mType == eEvent;
  }

  inline nsDOMEvent&
  GetAsEvent() const
  {
    MOZ_ASSERT(IsEvent(), "Wrong type!");
    return const_cast<NonNull<nsDOMEvent>&>(mValue.mEvent.Value());
  }

  inline NonNull<nsAString>&
  SetAsString()
  {
    mType = eString;
    return mValue.mString.SetValue();
  }

  inline bool
  IsString() const
  {
    return mType == eString;
  }

  inline const nsAString&
  GetAsString() const
  {
    MOZ_ASSERT(IsString(), "Wrong type!");
    return const_cast<NonNull<nsAString>&>(mValue.mString.Value());
  }

  bool
  ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::MutableHandle<JS::Value> rval) const;

private:
  inline void
  DestroyEvent()
  {
    MOZ_ASSERT(IsEvent(), "Wrong type!");
    mValue.mEvent.Destroy();
    mType = eUninitialized;
  }

  inline void
  DestroyString()
  {
    MOZ_ASSERT(IsString(), "Wrong type!");
    mValue.mString.Destroy();
    mType = eUninitialized;
  }
};

class HTMLElementOrLong
{
  friend class HTMLElementOrLongArgument;
  enum Type
  {
    eUninitialized,
    eHTMLElement,
    eLong
  };
  union Value
  {
    UnionMember<NonNull<nsGenericHTMLElement> > mHTMLElement;
    UnionMember<int32_t > mLong;
  };
  Type mType;
  Value mValue;

  HTMLElementOrLong(const HTMLElementOrLong&) MOZ_DELETE;
  void operator=(const HTMLElementOrLong) MOZ_DELETE;

public:
  explicit inline HTMLElementOrLong()
    : mType(eUninitialized)
  {
  }

  inline ~HTMLElementOrLong()
  {
    switch (mType) {
      case eUninitialized: {
        break;
      }
      case eHTMLElement: {
        DestroyHTMLElement();
        break;
      }
      case eLong: {
        DestroyLong();
        break;
      }
    }
  }

  inline NonNull<nsGenericHTMLElement>&
  SetAsHTMLElement()
  {
    mType = eHTMLElement;
    return mValue.mHTMLElement.SetValue();
  }

  inline bool
  IsHTMLElement() const
  {
    return mType == eHTMLElement;
  }

  inline nsGenericHTMLElement&
  GetAsHTMLElement() const
  {
    MOZ_ASSERT(IsHTMLElement(), "Wrong type!");
    return const_cast<NonNull<nsGenericHTMLElement>&>(mValue.mHTMLElement.Value());
  }

  inline int32_t&
  SetAsLong()
  {
    mType = eLong;
    return mValue.mLong.SetValue();
  }

  inline bool
  IsLong() const
  {
    return mType == eLong;
  }

  inline int32_t
  GetAsLong() const
  {
    MOZ_ASSERT(IsLong(), "Wrong type!");
    return const_cast<int32_t&>(mValue.mLong.Value());
  }

  bool
  ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::MutableHandle<JS::Value> rval) const;

private:
  inline void
  DestroyHTMLElement()
  {
    MOZ_ASSERT(IsHTMLElement(), "Wrong type!");
    mValue.mHTMLElement.Destroy();
    mType = eUninitialized;
  }

  inline void
  DestroyLong()
  {
    MOZ_ASSERT(IsLong(), "Wrong type!");
    mValue.mLong.Destroy();
    mType = eUninitialized;
  }
};

class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement
{
  friend class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementArgument;
  enum Type
  {
    eUninitialized,
    eHTMLImageElement,
    eHTMLCanvasElement,
    eHTMLVideoElement
  };
  union Value
  {
    UnionMember<NonNull<mozilla::dom::HTMLImageElement> > mHTMLImageElement;
    UnionMember<NonNull<mozilla::dom::HTMLCanvasElement> > mHTMLCanvasElement;
    UnionMember<NonNull<mozilla::dom::HTMLVideoElement> > mHTMLVideoElement;
  };
  Type mType;
  Value mValue;

  HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement(const HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement&) MOZ_DELETE;
  void operator=(const HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement) MOZ_DELETE;

public:
  explicit inline HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement()
    : mType(eUninitialized)
  {
  }

  inline ~HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement()
  {
    switch (mType) {
      case eUninitialized: {
        break;
      }
      case eHTMLImageElement: {
        DestroyHTMLImageElement();
        break;
      }
      case eHTMLCanvasElement: {
        DestroyHTMLCanvasElement();
        break;
      }
      case eHTMLVideoElement: {
        DestroyHTMLVideoElement();
        break;
      }
    }
  }

  inline NonNull<mozilla::dom::HTMLImageElement>&
  SetAsHTMLImageElement()
  {
    mType = eHTMLImageElement;
    return mValue.mHTMLImageElement.SetValue();
  }

  inline bool
  IsHTMLImageElement() const
  {
    return mType == eHTMLImageElement;
  }

  inline mozilla::dom::HTMLImageElement&
  GetAsHTMLImageElement() const
  {
    MOZ_ASSERT(IsHTMLImageElement(), "Wrong type!");
    return const_cast<NonNull<mozilla::dom::HTMLImageElement>&>(mValue.mHTMLImageElement.Value());
  }

  inline NonNull<mozilla::dom::HTMLCanvasElement>&
  SetAsHTMLCanvasElement()
  {
    mType = eHTMLCanvasElement;
    return mValue.mHTMLCanvasElement.SetValue();
  }

  inline bool
  IsHTMLCanvasElement() const
  {
    return mType == eHTMLCanvasElement;
  }

  inline mozilla::dom::HTMLCanvasElement&
  GetAsHTMLCanvasElement() const
  {
    MOZ_ASSERT(IsHTMLCanvasElement(), "Wrong type!");
    return const_cast<NonNull<mozilla::dom::HTMLCanvasElement>&>(mValue.mHTMLCanvasElement.Value());
  }

  inline NonNull<mozilla::dom::HTMLVideoElement>&
  SetAsHTMLVideoElement()
  {
    mType = eHTMLVideoElement;
    return mValue.mHTMLVideoElement.SetValue();
  }

  inline bool
  IsHTMLVideoElement() const
  {
    return mType == eHTMLVideoElement;
  }

  inline mozilla::dom::HTMLVideoElement&
  GetAsHTMLVideoElement() const
  {
    MOZ_ASSERT(IsHTMLVideoElement(), "Wrong type!");
    return const_cast<NonNull<mozilla::dom::HTMLVideoElement>&>(mValue.mHTMLVideoElement.Value());
  }

  bool
  ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::MutableHandle<JS::Value> rval) const;

private:
  inline void
  DestroyHTMLImageElement()
  {
    MOZ_ASSERT(IsHTMLImageElement(), "Wrong type!");
    mValue.mHTMLImageElement.Destroy();
    mType = eUninitialized;
  }

  inline void
  DestroyHTMLCanvasElement()
  {
    MOZ_ASSERT(IsHTMLCanvasElement(), "Wrong type!");
    mValue.mHTMLCanvasElement.Destroy();
    mType = eUninitialized;
  }

  inline void
  DestroyHTMLVideoElement()
  {
    MOZ_ASSERT(IsHTMLVideoElement(), "Wrong type!");
    mValue.mHTMLVideoElement.Destroy();
    mType = eUninitialized;
  }
};

class HTMLOptionElementOrHTMLOptGroupElement
{
  friend class HTMLOptionElementOrHTMLOptGroupElementArgument;
  enum Type
  {
    eUninitialized,
    eHTMLOptionElement,
    eHTMLOptGroupElement
  };
  union Value
  {
    UnionMember<NonNull<mozilla::dom::HTMLOptionElement> > mHTMLOptionElement;
    UnionMember<NonNull<mozilla::dom::HTMLOptGroupElement> > mHTMLOptGroupElement;
  };
  Type mType;
  Value mValue;

  HTMLOptionElementOrHTMLOptGroupElement(const HTMLOptionElementOrHTMLOptGroupElement&) MOZ_DELETE;
  void operator=(const HTMLOptionElementOrHTMLOptGroupElement) MOZ_DELETE;

public:
  explicit inline HTMLOptionElementOrHTMLOptGroupElement()
    : mType(eUninitialized)
  {
  }

  inline ~HTMLOptionElementOrHTMLOptGroupElement()
  {
    switch (mType) {
      case eUninitialized: {
        break;
      }
      case eHTMLOptionElement: {
        DestroyHTMLOptionElement();
        break;
      }
      case eHTMLOptGroupElement: {
        DestroyHTMLOptGroupElement();
        break;
      }
    }
  }

  inline NonNull<mozilla::dom::HTMLOptionElement>&
  SetAsHTMLOptionElement()
  {
    mType = eHTMLOptionElement;
    return mValue.mHTMLOptionElement.SetValue();
  }

  inline bool
  IsHTMLOptionElement() const
  {
    return mType == eHTMLOptionElement;
  }

  inline mozilla::dom::HTMLOptionElement&
  GetAsHTMLOptionElement() const
  {
    MOZ_ASSERT(IsHTMLOptionElement(), "Wrong type!");
    return const_cast<NonNull<mozilla::dom::HTMLOptionElement>&>(mValue.mHTMLOptionElement.Value());
  }

  inline NonNull<mozilla::dom::HTMLOptGroupElement>&
  SetAsHTMLOptGroupElement()
  {
    mType = eHTMLOptGroupElement;
    return mValue.mHTMLOptGroupElement.SetValue();
  }

  inline bool
  IsHTMLOptGroupElement() const
  {
    return mType == eHTMLOptGroupElement;
  }

  inline mozilla::dom::HTMLOptGroupElement&
  GetAsHTMLOptGroupElement() const
  {
    MOZ_ASSERT(IsHTMLOptGroupElement(), "Wrong type!");
    return const_cast<NonNull<mozilla::dom::HTMLOptGroupElement>&>(mValue.mHTMLOptGroupElement.Value());
  }

  bool
  ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::MutableHandle<JS::Value> rval) const;

private:
  inline void
  DestroyHTMLOptionElement()
  {
    MOZ_ASSERT(IsHTMLOptionElement(), "Wrong type!");
    mValue.mHTMLOptionElement.Destroy();
    mType = eUninitialized;
  }

  inline void
  DestroyHTMLOptGroupElement()
  {
    MOZ_ASSERT(IsHTMLOptGroupElement(), "Wrong type!");
    mValue.mHTMLOptGroupElement.Destroy();
    mType = eUninitialized;
  }
};

class StringOrCanvasGradientOrCanvasPattern
{
  friend class StringOrCanvasGradientOrCanvasPatternArgument;
  enum Type
  {
    eUninitialized,
    eString,
    eCanvasGradient,
    eCanvasPattern
  };
  union Value
  {
    UnionMember<NonNull<nsAString> > mString;
    UnionMember<NonNull<mozilla::dom::CanvasGradient> > mCanvasGradient;
    UnionMember<NonNull<mozilla::dom::CanvasPattern> > mCanvasPattern;
  };
  Type mType;
  Value mValue;

  StringOrCanvasGradientOrCanvasPattern(const StringOrCanvasGradientOrCanvasPattern&) MOZ_DELETE;
  void operator=(const StringOrCanvasGradientOrCanvasPattern) MOZ_DELETE;

public:
  explicit inline StringOrCanvasGradientOrCanvasPattern()
    : mType(eUninitialized)
  {
  }

  inline ~StringOrCanvasGradientOrCanvasPattern()
  {
    switch (mType) {
      case eUninitialized: {
        break;
      }
      case eString: {
        DestroyString();
        break;
      }
      case eCanvasGradient: {
        DestroyCanvasGradient();
        break;
      }
      case eCanvasPattern: {
        DestroyCanvasPattern();
        break;
      }
    }
  }

  inline NonNull<nsAString>&
  SetAsString()
  {
    mType = eString;
    return mValue.mString.SetValue();
  }

  inline bool
  IsString() const
  {
    return mType == eString;
  }

  inline const nsAString&
  GetAsString() const
  {
    MOZ_ASSERT(IsString(), "Wrong type!");
    return const_cast<NonNull<nsAString>&>(mValue.mString.Value());
  }

  inline NonNull<mozilla::dom::CanvasGradient>&
  SetAsCanvasGradient()
  {
    mType = eCanvasGradient;
    return mValue.mCanvasGradient.SetValue();
  }

  inline bool
  IsCanvasGradient() const
  {
    return mType == eCanvasGradient;
  }

  inline mozilla::dom::CanvasGradient&
  GetAsCanvasGradient() const
  {
    MOZ_ASSERT(IsCanvasGradient(), "Wrong type!");
    return const_cast<NonNull<mozilla::dom::CanvasGradient>&>(mValue.mCanvasGradient.Value());
  }

  inline NonNull<mozilla::dom::CanvasPattern>&
  SetAsCanvasPattern()
  {
    mType = eCanvasPattern;
    return mValue.mCanvasPattern.SetValue();
  }

  inline bool
  IsCanvasPattern() const
  {
    return mType == eCanvasPattern;
  }

  inline mozilla::dom::CanvasPattern&
  GetAsCanvasPattern() const
  {
    MOZ_ASSERT(IsCanvasPattern(), "Wrong type!");
    return const_cast<NonNull<mozilla::dom::CanvasPattern>&>(mValue.mCanvasPattern.Value());
  }

  bool
  ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::MutableHandle<JS::Value> rval) const;

private:
  inline void
  DestroyString()
  {
    MOZ_ASSERT(IsString(), "Wrong type!");
    mValue.mString.Destroy();
    mType = eUninitialized;
  }

  inline void
  DestroyCanvasGradient()
  {
    MOZ_ASSERT(IsCanvasGradient(), "Wrong type!");
    mValue.mCanvasGradient.Destroy();
    mType = eUninitialized;
  }

  inline void
  DestroyCanvasPattern()
  {
    MOZ_ASSERT(IsCanvasPattern(), "Wrong type!");
    mValue.mCanvasPattern.Destroy();
    mType = eUninitialized;
  }
};

class EventOrStringReturnValue
{
  enum Type
  {
    eUninitialized,
    eEvent,
    eString
  };
  union Value
  {
    UnionMember<OwningNonNull<nsDOMEvent> > mEvent;
    UnionMember<nsString > mString;
  };
  Type mType;
  Value mValue;

  EventOrStringReturnValue(const EventOrStringReturnValue&) MOZ_DELETE;
  void operator=(const EventOrStringReturnValue) MOZ_DELETE;

public:
  explicit inline EventOrStringReturnValue()
    : mType(eUninitialized)
  {
  }

  ~EventOrStringReturnValue();

  OwningNonNull<nsDOMEvent>&
  SetAsEvent();

  inline bool
  IsEvent() const
  {
    return mType == eEvent;
  }

  nsString&
  SetAsString();

  inline bool
  IsString() const
  {
    return mType == eString;
  }

  bool
  ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::MutableHandle<JS::Value> rval) const;

private:
  void
  DestroyEvent();

  void
  DestroyString();
};

class HTMLElementOrLongReturnValue
{
  enum Type
  {
    eUninitialized,
    eHTMLElement,
    eLong
  };
  union Value
  {
    UnionMember<OwningNonNull<nsGenericHTMLElement> > mHTMLElement;
    UnionMember<int32_t > mLong;
  };
  Type mType;
  Value mValue;

  HTMLElementOrLongReturnValue(const HTMLElementOrLongReturnValue&) MOZ_DELETE;
  void operator=(const HTMLElementOrLongReturnValue) MOZ_DELETE;

public:
  explicit inline HTMLElementOrLongReturnValue()
    : mType(eUninitialized)
  {
  }

  ~HTMLElementOrLongReturnValue();

  OwningNonNull<nsGenericHTMLElement>&
  SetAsHTMLElement();

  inline bool
  IsHTMLElement() const
  {
    return mType == eHTMLElement;
  }

  int32_t&
  SetAsLong();

  inline bool
  IsLong() const
  {
    return mType == eLong;
  }

  bool
  ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::MutableHandle<JS::Value> rval) const;

private:
  void
  DestroyHTMLElement();

  void
  DestroyLong();
};

class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementReturnValue
{
  enum Type
  {
    eUninitialized,
    eHTMLImageElement,
    eHTMLCanvasElement,
    eHTMLVideoElement
  };
  union Value
  {
    UnionMember<OwningNonNull<mozilla::dom::HTMLImageElement> > mHTMLImageElement;
    UnionMember<OwningNonNull<mozilla::dom::HTMLCanvasElement> > mHTMLCanvasElement;
    UnionMember<OwningNonNull<mozilla::dom::HTMLVideoElement> > mHTMLVideoElement;
  };
  Type mType;
  Value mValue;

  HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementReturnValue(const HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementReturnValue&) MOZ_DELETE;
  void operator=(const HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementReturnValue) MOZ_DELETE;

public:
  explicit inline HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementReturnValue()
    : mType(eUninitialized)
  {
  }

  ~HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementReturnValue();

  OwningNonNull<mozilla::dom::HTMLImageElement>&
  SetAsHTMLImageElement();

  inline bool
  IsHTMLImageElement() const
  {
    return mType == eHTMLImageElement;
  }

  OwningNonNull<mozilla::dom::HTMLCanvasElement>&
  SetAsHTMLCanvasElement();

  inline bool
  IsHTMLCanvasElement() const
  {
    return mType == eHTMLCanvasElement;
  }

  OwningNonNull<mozilla::dom::HTMLVideoElement>&
  SetAsHTMLVideoElement();

  inline bool
  IsHTMLVideoElement() const
  {
    return mType == eHTMLVideoElement;
  }

  bool
  ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::MutableHandle<JS::Value> rval) const;

private:
  void
  DestroyHTMLImageElement();

  void
  DestroyHTMLCanvasElement();

  void
  DestroyHTMLVideoElement();
};

class HTMLOptionElementOrHTMLOptGroupElementReturnValue
{
  enum Type
  {
    eUninitialized,
    eHTMLOptionElement,
    eHTMLOptGroupElement
  };
  union Value
  {
    UnionMember<OwningNonNull<mozilla::dom::HTMLOptionElement> > mHTMLOptionElement;
    UnionMember<OwningNonNull<mozilla::dom::HTMLOptGroupElement> > mHTMLOptGroupElement;
  };
  Type mType;
  Value mValue;

  HTMLOptionElementOrHTMLOptGroupElementReturnValue(const HTMLOptionElementOrHTMLOptGroupElementReturnValue&) MOZ_DELETE;
  void operator=(const HTMLOptionElementOrHTMLOptGroupElementReturnValue) MOZ_DELETE;

public:
  explicit inline HTMLOptionElementOrHTMLOptGroupElementReturnValue()
    : mType(eUninitialized)
  {
  }

  ~HTMLOptionElementOrHTMLOptGroupElementReturnValue();

  OwningNonNull<mozilla::dom::HTMLOptionElement>&
  SetAsHTMLOptionElement();

  inline bool
  IsHTMLOptionElement() const
  {
    return mType == eHTMLOptionElement;
  }

  OwningNonNull<mozilla::dom::HTMLOptGroupElement>&
  SetAsHTMLOptGroupElement();

  inline bool
  IsHTMLOptGroupElement() const
  {
    return mType == eHTMLOptGroupElement;
  }

  bool
  ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::MutableHandle<JS::Value> rval) const;

private:
  void
  DestroyHTMLOptionElement();

  void
  DestroyHTMLOptGroupElement();
};

class StringOrCanvasGradientOrCanvasPatternReturnValue
{
  enum Type
  {
    eUninitialized,
    eString,
    eCanvasGradient,
    eCanvasPattern
  };
  union Value
  {
    UnionMember<nsString > mString;
    UnionMember<OwningNonNull<mozilla::dom::CanvasGradient> > mCanvasGradient;
    UnionMember<OwningNonNull<mozilla::dom::CanvasPattern> > mCanvasPattern;
  };
  Type mType;
  Value mValue;

  StringOrCanvasGradientOrCanvasPatternReturnValue(const StringOrCanvasGradientOrCanvasPatternReturnValue&) MOZ_DELETE;
  void operator=(const StringOrCanvasGradientOrCanvasPatternReturnValue) MOZ_DELETE;

public:
  explicit inline StringOrCanvasGradientOrCanvasPatternReturnValue()
    : mType(eUninitialized)
  {
  }

  ~StringOrCanvasGradientOrCanvasPatternReturnValue();

  nsString&
  SetAsString();

  inline bool
  IsString() const
  {
    return mType == eString;
  }

  OwningNonNull<mozilla::dom::CanvasGradient>&
  SetAsCanvasGradient();

  inline bool
  IsCanvasGradient() const
  {
    return mType == eCanvasGradient;
  }

  OwningNonNull<mozilla::dom::CanvasPattern>&
  SetAsCanvasPattern();

  inline bool
  IsCanvasPattern() const
  {
    return mType == eCanvasPattern;
  }

  bool
  ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::MutableHandle<JS::Value> rval) const;

private:
  void
  DestroyString();

  void
  DestroyCanvasGradient();

  void
  DestroyCanvasPattern();
};
} // namespace dom
} // namespace mozilla


#endif // mozilla_dom_UnionTypes_h__
