package ocelot.desktop.util

import java.lang.reflect.Constructor
import scala.collection.mutable.ArrayBuffer

object ReflectionUtils {

  /** Returns the linearization order of a class.
    *
    * Roughly speaking, it's the order method calls are resolved in.
    *
    * It also acts as a list of superclasses (each occurring once).
    */
  def linearizationOrder(clazz: Class[_]): Iterator[Class[_]] = {
    val result = ArrayBuffer[Class[_]](clazz)

    for (interface <- clazz.getInterfaces.reverseIterator) {
      result ++= linearizationOrder(interface)
    }

    for (superclass <- Option(clazz.getSuperclass)) {
      result ++= linearizationOrder(superclass)
    }

    val merged = result.reverseIterator.distinct.toBuffer

    merged.reverseIterator
  }

  /** Finds a (most specific) unary constructor of `constructedClass` that accepts `argumentClass` (or its subtype). */
  def findUnaryConstructor[A](constructedClass: Class[A], argumentClass: Class[_]): Option[Constructor[A]] = {
    try {
      // happy case: just grab the constructor directly
      return Some(constructedClass.getConstructor(argumentClass))
    } catch {
      case _: NoSuchMethodException =>
      // uh-oh, we'll have to try harder...
    }

    val constructors = constructedClass.getConstructors
      .iterator
      .filter(_.getParameterCount == 1)
      .map(_.asInstanceOf[Constructor[A]])
      .map(constructor => (constructor.getParameterTypes()(0), constructor))
      .toMap

    linearizationOrder(argumentClass)
      .flatMap(constructors.get)
      .nextOption()
  }
}
