package de.kuschku.libquassel.protocol

class QVariant<T>(val data: T?, val type: MetaType<T>) {
  constructor(data: T?, type: Type) : this(data, MetaType.Companion.get(type))
  constructor(data: T?, type: QType) : this(data, type.typeName)
  constructor(data: T?, type: String) : this(data, MetaType.Companion.get(type))

  private fun <U> coerce(): QVariant<U> {
    return this as QVariant<U>
  }

  fun or(defValue: T): T {
    return data ?: defValue
  }

  fun <U> _value(defValue: U): U {
    return this.coerce<U>().data ?: defValue
  }

  fun <U> _valueOr(f: () -> U): U {
    return this.coerce<U>().data ?: f()
  }

  fun <U> _valueOrThrow(): U = this._valueOrThrow(NullPointerException())

  fun <U> _valueOrThrow(e: Throwable): U {
    return this.coerce<U>().data ?: throw e
  }

  override fun toString(): String {
    return "QVariant(${type.name}, $data)"
  }
}

fun <U> QVariant_?.value(): U?
  = this?._value<U?>(null)

fun <U> QVariant_?.value(defValue: U): U
  = this?._value(defValue) ?: defValue

fun <U> QVariant_?.valueOr(f: () -> U): U
  = this?._valueOr(f) ?: f()

fun <U> QVariant_?.valueOrThrow(e: Throwable = NullPointerException()): U
  = this?._valueOrThrow<U>(e) ?: throw e