#ifndef mozilla_dom_UnionTypes_h__
#define mozilla_dom_UnionTypes_h__

#include "mozilla/dom/BindingUtils.h"

class nsHTMLCanvasElement;
class nsHTMLImageElement;
class nsHTMLOptGroupElement;
class nsHTMLOptionElement;
class nsHTMLVideoElement;
class nsIDOMHTMLElement;

namespace mozilla {
namespace dom {

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

  bool IsHTMLElement() const
  {
    return mType == eHTMLElement;
  }
  nsIDOMHTMLElement* GetAsHTMLElement() const
  {
    MOZ_ASSERT(IsHTMLElement(), "Wrong type!");
    // The cast to nsIDOMHTMLElement* is needed to work around a bug in Apple's
    // clang compiler, for some reason it doesn't call |S::operator T&| when
    // casting S<T> to T& and T is forward declared.
    return (nsIDOMHTMLElement*)mValue.mHTMLElement.Value();
  }
  nsIDOMHTMLElement*& SetAsHTMLElement()
  {
    mType = eHTMLElement;
    return mValue.mHTMLElement.SetValue();
  }

  bool IsLong() const
  {
    return mType == eLong;
  }
  int32_t GetAsLong() const
  {
    MOZ_ASSERT(IsLong(), "Wrong type!");
    // The cast to int32_t is needed to work around a bug in Apple's
    // clang compiler, for some reason it doesn't call |S::operator T&| when
    // casting S<T> to T& and T is forward declared.
    return (int32_t)mValue.mLong.Value();
  }
  int32_t& SetAsLong()
  {
    mType = eLong;
    return mValue.mLong.SetValue();
  }

private:
  friend class HTMLElementOrLongArgument;

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

  enum Type {
    eUninitialized,
    eHTMLElement,
    eLong
  };
  union Value {
    UnionMember<nsIDOMHTMLElement* > mHTMLElement;
    UnionMember<int32_t > mLong;
  };

  Type mType;
  Value mValue;
};



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

  bool IsHTMLImageElement() const
  {
    return mType == eHTMLImageElement;
  }
  nsHTMLImageElement* GetAsHTMLImageElement() const
  {
    MOZ_ASSERT(IsHTMLImageElement(), "Wrong type!");
    // The cast to nsHTMLImageElement* is needed to work around a bug in Apple's
    // clang compiler, for some reason it doesn't call |S::operator T&| when
    // casting S<T> to T& and T is forward declared.
    return (nsHTMLImageElement*)mValue.mHTMLImageElement.Value();
  }
  nsHTMLImageElement*& SetAsHTMLImageElement()
  {
    mType = eHTMLImageElement;
    return mValue.mHTMLImageElement.SetValue();
  }

  bool IsHTMLCanvasElement() const
  {
    return mType == eHTMLCanvasElement;
  }
  nsHTMLCanvasElement* GetAsHTMLCanvasElement() const
  {
    MOZ_ASSERT(IsHTMLCanvasElement(), "Wrong type!");
    // The cast to nsHTMLCanvasElement* is needed to work around a bug in Apple's
    // clang compiler, for some reason it doesn't call |S::operator T&| when
    // casting S<T> to T& and T is forward declared.
    return (nsHTMLCanvasElement*)mValue.mHTMLCanvasElement.Value();
  }
  nsHTMLCanvasElement*& SetAsHTMLCanvasElement()
  {
    mType = eHTMLCanvasElement;
    return mValue.mHTMLCanvasElement.SetValue();
  }

  bool IsHTMLVideoElement() const
  {
    return mType == eHTMLVideoElement;
  }
  nsHTMLVideoElement* GetAsHTMLVideoElement() const
  {
    MOZ_ASSERT(IsHTMLVideoElement(), "Wrong type!");
    // The cast to nsHTMLVideoElement* is needed to work around a bug in Apple's
    // clang compiler, for some reason it doesn't call |S::operator T&| when
    // casting S<T> to T& and T is forward declared.
    return (nsHTMLVideoElement*)mValue.mHTMLVideoElement.Value();
  }
  nsHTMLVideoElement*& SetAsHTMLVideoElement()
  {
    mType = eHTMLVideoElement;
    return mValue.mHTMLVideoElement.SetValue();
  }

private:
  friend class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementArgument;

  void DestroyHTMLImageElement()
  {
    MOZ_ASSERT(IsHTMLImageElement(), "Wrong type!");
    mValue.mHTMLImageElement.Destroy();
    mType = eUninitialized;
  }
  void DestroyHTMLCanvasElement()
  {
    MOZ_ASSERT(IsHTMLCanvasElement(), "Wrong type!");
    mValue.mHTMLCanvasElement.Destroy();
    mType = eUninitialized;
  }
  void DestroyHTMLVideoElement()
  {
    MOZ_ASSERT(IsHTMLVideoElement(), "Wrong type!");
    mValue.mHTMLVideoElement.Destroy();
    mType = eUninitialized;
  }

  enum Type {
    eUninitialized,
    eHTMLImageElement,
    eHTMLCanvasElement,
    eHTMLVideoElement
  };
  union Value {
    UnionMember<nsHTMLImageElement* > mHTMLImageElement;
    UnionMember<nsHTMLCanvasElement* > mHTMLCanvasElement;
    UnionMember<nsHTMLVideoElement* > mHTMLVideoElement;
  };

  Type mType;
  Value mValue;
};



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

  bool IsHTMLOptionElement() const
  {
    return mType == eHTMLOptionElement;
  }
  nsHTMLOptionElement* GetAsHTMLOptionElement() const
  {
    MOZ_ASSERT(IsHTMLOptionElement(), "Wrong type!");
    // The cast to nsHTMLOptionElement* is needed to work around a bug in Apple's
    // clang compiler, for some reason it doesn't call |S::operator T&| when
    // casting S<T> to T& and T is forward declared.
    return (nsHTMLOptionElement*)mValue.mHTMLOptionElement.Value();
  }
  nsHTMLOptionElement*& SetAsHTMLOptionElement()
  {
    mType = eHTMLOptionElement;
    return mValue.mHTMLOptionElement.SetValue();
  }

  bool IsHTMLOptGroupElement() const
  {
    return mType == eHTMLOptGroupElement;
  }
  nsHTMLOptGroupElement* GetAsHTMLOptGroupElement() const
  {
    MOZ_ASSERT(IsHTMLOptGroupElement(), "Wrong type!");
    // The cast to nsHTMLOptGroupElement* is needed to work around a bug in Apple's
    // clang compiler, for some reason it doesn't call |S::operator T&| when
    // casting S<T> to T& and T is forward declared.
    return (nsHTMLOptGroupElement*)mValue.mHTMLOptGroupElement.Value();
  }
  nsHTMLOptGroupElement*& SetAsHTMLOptGroupElement()
  {
    mType = eHTMLOptGroupElement;
    return mValue.mHTMLOptGroupElement.SetValue();
  }

private:
  friend class HTMLOptionElementOrHTMLOptGroupElementArgument;

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

  enum Type {
    eUninitialized,
    eHTMLOptionElement,
    eHTMLOptGroupElement
  };
  union Value {
    UnionMember<nsHTMLOptionElement* > mHTMLOptionElement;
    UnionMember<nsHTMLOptGroupElement* > mHTMLOptGroupElement;
  };

  Type mType;
  Value mValue;
};

} // namespace dom
} // namespace mozilla


#endif // mozilla_dom_UnionTypes_h__
