class Class
  def inspect(io : IO) : Nil
    to_s(io)
  end

  # See `Object#hash(hasher)`
  def hash(hasher)
    hasher.class(self)
  end

  # Returns whether this class is the same as *other*.
  #
  # ```
  # Int32 == Int32  # => true
  # Int32 == String # => false
  # ```
  def ==(other : Class) : Bool
    crystal_type_id == other.crystal_type_id
  end

  # Returns whether this class inherits or includes *other*.
  #
  # ```
  # Int32 < Number  # => true
  # Int32 < Value   # => true
  # Int32 < Int32   # => false
  # Int32 <= String # => false
  # ```
  def <(other : T.class) : Bool forall T
    # This is so that the method is expanded differently for each type
    {% @type %}
    other._gt(self)
  end

  # Returns whether this class inherits or includes *other*, or
  # is equal to *other*.
  #
  # ```
  # Int32 < Number  # => true
  # Int32 < Value   # => true
  # Int32 <= Int32  # => true
  # Int32 <= String # => false
  # ```
  def <=(other : T.class) : Bool forall T
    # This is so that the method is expanded differently for each type
    {% @type %}
    other._gte(self)
  end

  # Returns whether *other* inherits or includes `self`.
  #
  # ```
  # Number > Int32  # => true
  # Number > Number # => false
  # Number > Object # => false
  # ```
  def >(other : T.class) : Bool forall T
    # This is so that the method is expanded differently for each type
    {% @type %}
    other._lt(self)
  end

  # Returns whether *other* inherits or includes `self`, or is equal
  # to `self`.
  #
  # ```
  # Number >= Int32  # => true
  # Number >= Number # => true
  # Number >= Object # => false
  # ```
  def >=(other : T.class) forall T
    # This is so that the method is expanded differently for each type
    {% @type %}
    other._lte(self)
  end

  # :nodoc:
  def _lt(other : T.class) forall T
    {{ @type < T }}
  end

  # :nodoc:
  def _lte(other : T.class) forall T
    {{ @type <= T }}
  end

  # :nodoc:
  def _gt(other : T.class) forall T
    {{ @type > T }}
  end

  # :nodoc:
  def _gte(other : T.class) forall T
    {{ @type >= T }}
  end

  def ===(other)
    # This branch handles `Int32.class === 1` case.
    # In this case, `@type` is `Class` and `other.is_a?(self)` means `other.is_a?(Object)`
    # because type of `self` is an instance type of the scope type and the instance type of `Class` is `Object`.
    # See https://github.com/crystal-lang/crystal/issues/10736.
    {% if @type == Class %}
      other.is_a?(Class)
    {% else %}
      other.is_a?(self)
    {% end %}
  end

  # Returns the name of this class.
  #
  # ```
  # String.name # => "String"
  # ```
  def name : String
    {{ @type.name.stringify }}
  end

  # Casts *other* to this class.
  #
  # This is the same as using `as`, but allows the class to be passed around as
  # an argument. See the
  # [documentation on as](//crystal-lang.org/docs/syntax_and_semantics/as.html)
  # for more information.
  #
  # ```
  # klass = Int32
  # number = [99, "str"][0]
  # typeof(number)             # => (String | Int32)
  # typeof(klass.cast(number)) # => Int32
  # ```
  def cast(other) : self
    other.as(self)
  end

  # Returns the union type of `self` and *other*.
  #
  # ```
  # Int32 | Char # => (Int32 | Char)
  # ```
  def self.|(other : U.class) forall U
    t = uninitialized self
    u = uninitialized U
    typeof(t, u)
  end

  # Returns `true` if `nil` is an instance of this type.
  #
  # ```
  # Int32.nilable?            # => false
  # Nil.nilable?              # => true
  # (Int32 | String).nilable? # => false
  # (Int32 | Nil).nilable?    # => true
  # NoReturn.nilable?         # => false
  # Value.nilable?            # => true
  # ```
  def nilable? : Bool
    {{ @type >= Nil }}
  end

  def to_s(io : IO) : Nil
    io << {{ @type.name.stringify }}
  end

  def dup
    self
  end

  def clone
    self
  end
end
