/**反射：从字节码（.class文件）中获取类、方法、属性，并调用使用之的办法
 * 1.类获取---MyClass::class
 * 2.实例生成（构造器调用）---createInstance();::MyClass;call()
 * 3.函数调用---MyClass::functionName,invoke(instance,args);call()
 * 4.属性使用---MyClass::propewrtyName,get(instance),set(instance,value);
 */
import kotlin.reflect.*
import kotlin.reflect.full.*
import kotlin.reflect.jvm.javaMethod
import kotlin.reflect.jvm.javaConstructor
import kotlin.reflect.jvm.javaField
import kotlin.reflect.jvm.javaGetter
import kotlin.reflect.jvm.javaSetter

//顶层属性
var topStr = "顶层测试属性"
//顶层函数
fun isSmall(i: Int) = i < 5
fun isSmall(s: String) = s.length < 5

fun main() {
    println("--------------------Reflect Class-----------------")
    val c1 = ReflectClass::class
    println(c1.simpleName)

    val o2=ReflectClass("Alice")
    val c2=o2::class
    println(c2.simpleName)
    
    val c3=o2.javaClass.kotlin
    println(c3.simpleName)

    getReflectClassInfo()


    println("--------------------Reflect Instance-----------------")
    // 调用无参数的构造器创建实例
    val inst1 = c1.createInstance()
    println("createInstance:${inst1.name}  ====  ${inst1.age}")
    // 调用主构造器创建实例
    val inst2 =c1.primaryConstructor?.call(18)
    println("primaryConstructor:${inst2?.name}  ====  ${inst2?.age}")
    // 调用某一种参数的构造器创建实例
    val constructor: (Int) -> ReflectClass = ::ReflectClass
    val inst3 = constructor(66)
    println("::NyClass:${inst3.name}  ====  ${inst3.age}")
    //调用constructor创建实例
    callReflectClassConstructor()

    println("--------------------Reflect Function-----------------")
    //成员函数调用
    val f1 = ReflectClass::test
    f1.invoke(ReflectClass(),"Java",55.1)
    val javaFunction=f1.javaMethod
    val f2:ReflectClass.()->Unit = ReflectClass::info
    f2.invoke(ReflectClass())
    //顶层函数调用
    val f3:(Int)->Boolean =::isSmall
    println(f3.invoke(4))
    var f4: (String) -> Boolean = ::isSmall
    val strlist = listOf("Java" , "Kotlin", "Swift", "Go", "Erlang")
    val resultStrList = strlist.filter(f4)
    /*等效替换
    val resultStrList = strlist.filter(::isSmall)*/
    println(resultStrList)
    //扩展函数调用
    val f5 = ReflectClass::bar
    f5.invoke(ReflectClass())

    callReflectFunction()

    println("--------------------Reflect Property----------------")
    // 获取f顶层属性，属于KMutableProperty0的实例
    val topProp = ::topStr
    topProp.set("修改后的属性")
    println(topProp.get())

    val im = ReflectClass()
    // 获取name属性，属于KMutableProperty1的实例
    val mp =  ReflectClass::name
    mp.set(im, "赵六")
    println(mp.get(im))

    // 获取成员属性，属于KProperty1的实例
    val prop =  ReflectClass::age
    println(prop.get(im))

    // 获取扩展属性，属于KProperty1的实例
    val extProp =  ReflectClass::foo
    println(extProp.get(im))

    callReflectProperty()
    println("--------------------Java Interoperation---------------")
    javaInteroperation()
}

// 定义注解
annotation class Anno
// 使用3个注解修饰该类
@Anno
@Suppress("UNCHECKED_CAST")
class ReflectClass(val age: Int){
    var name: String = "张三"
    // 为该类定义一个私有的构造器
    constructor(): this(20){
        println("执行无参数的构造器:${name}   ${age}")
    }
    // 定义一个有参数的构造器
    constructor(name: String): this(15){
        println("执行有一个参数的构造器:${name}")
        this.name=name
    }
    // 定义一个有两个参数的构造器
    constructor(name: String,age: Int): this(age){
        println("执行有两个参数的构造器:${name}   ${age}")
        this.name=name
//        this.age=age
    }
    // 定义一个无参数的info方法
    fun info(){
        println("执行无参数的info方法")
    }
    // 定义一个有参数的info方法
    fun info(str: String){
        println("执行有参数的info方法"
                + "，其str参数值：" + str)
    }

    fun test(msg: String, price: Double){
        println("执行带String, Double参数的test方法：${msg}, ${price}")
    }

    // 定义一个测试用的嵌套类
    class Inner
}

// 为ReflectClass定义扩展方法
fun ReflectClass.bar() {
    println("扩展的bar方法")
}

// 为ReflectClass定义扩展属性
val ReflectClass.foo: Double
    get() = 2.4

fun getReflectClassInfo(){
    // 下面代码可以获取ReflectClass对应的KClass
    val clazz = ReflectClass::class
    // 通过constructors属性获取KClass对象所对应类的全部构造器
    val ctors = clazz.constructors;
    println("ReflectClass的全部构造器如下：")
    ctors.forEach { println(it) }
    println("ReflectClass的主构造器如下：")
    println(clazz.primaryConstructor)

    // 通过functions属性获取该KClass对象所对应类的全部方法
    var funs = clazz.functions
    println("ReflectClass的全部方法如下：")
    funs.forEach { println(it) }

    /*
    通过declaredFunctions属性获取该KClass对象
    本身所声明的全部方法（不包括继承的方法） */
    var funs2 = clazz.declaredFunctions
    println("ReflectClass本身声明的全部方法如下：")
    funs2.forEach { println(it) }

    /*
    通过declaredMemberFunctions属性获取该KClass对象
    本身所声明的全部成员方法（不包括继承的方法） */
    var memberFunctions = clazz.declaredMemberFunctions
    println("ReflectClass本身声明的成员方法如下：")
    memberFunctions.forEach { println(it) }

    /*
    通过memberExtensionFunctions属性获取该KClass对象
    所代表类的全部扩展方法（不包括继承的方法） */
    var extensionFunctions = clazz.memberExtensionFunctions
    println("ReflectClass的扩展方法如下：")
    extensionFunctions.forEach { println(it) }

    /*
    通过declaredMemberProperties属性获取该KClass对象
    本身所声明的全部成员属性（不包括继承的属性） */
    var memberProperties = clazz.declaredMemberProperties
    println("ReflectClass本身声明的成员属性如下：")
    memberProperties.forEach { println(it) }

    /*
    通过memberExtensionProperties属性获取该KClass对象
    所代表类的全部扩展属性（不包括继承的属性） */
    var extensionProperties = clazz.memberExtensionProperties
    println("ReflectClass的扩展属性如下：")
    extensionProperties.forEach { println(it) }

    // 通过annotations属性获取该KClass对象所对应类的全部注解
    val anns = clazz.annotations;
    println("ReflectClass的全部注解如下：")
    anns.forEach { println(it) }
    println("该KClass元素上的@Anno注解为："
            + clazz.findAnnotation<Anno>())
    // 通过nestedClasses属性获取该KClass对象所对应类的全部嵌套类（包括内部类和嵌套类）
    val inners = clazz.nestedClasses
    System.out.println("ReflectClass的全部内部类如下：")
    inners.forEach { println(it) }
    // 通过supertypes属性获取该类的所有父类型（包括父类和父接口）
    println("ReflectClass的父类型为：" + clazz.supertypes) 
}

fun callReflectClassConstructor() {
    val clazz=ReflectClass::class
    val cons = clazz.constructors
    cons.forEach {
        if(it.parameters.size == 2) {
            // 调用带2个参数的构造器创建实例
            val inst = it.call("李四",10)
            println("调用带2个参数的构造器:${inst.name}  ====  ${inst.age}")
        }

        if(it.parameters.size == 1 && it.parameters[0].name=="name") {
            // 调用带1个参数且参数名称为name的构造器创建实例
            val inst = it.call("王五")
            println("调用带1个参数且参数名称为name的构造器:${inst.name}  ====  ${inst.age}")
        }
    }
}

fun abs(d: Double): Double = if (d < 0) -d else d
fun sqrt(d: Double): Double = Math.sqrt(d)
// 定义一个comp()函数，该函数用于将两个函数组合起来
fun comp(fun1: (Double) -> Double, fun2: (Double) -> Double) :
            (Double) -> Double{
    return { x -> fun2(fun1(x)) }
}
//call的第一个参数必须是对象，参数数量变为N+1
fun callReflectFunction() {
    val clazz=ReflectClass::class
    val ins = clazz.createInstance()
    // 获取clazz所代表类直接定义的全部函数
    val funs = clazz.declaredFunctions
    for (f in funs) {
        // 如果函数具有3个参数（对应2个参数的方法）
        if (f.parameters.size == 3) {
            // 调用带3个参数的函数
            f.call(ins, "Kotlin", 78.8)
        }
        // 如果函数具有2个参数（对应1个参数的方法）
        if (f.parameters.size == 2) {
            // 调用带2个参数的函数
            f.call(ins, "Kotlin")
        }
    }

    // 将::abs和::sqrt组合起来
    val f = comp(::abs, ::sqrt)
    println(f(-25.0))
}

//KProperty/KMutableProperty:通用属性/读写属性
//KProperty()/KMutableProperty():无调用者的静态属性/无调用者的静态读写属性
//KProperty1/KMutableProperty1:1个调用者的成员属性/1个调用者的成员读写属性
//KProperty2/KMutableProperty2:3个调用者的扩展属性/2个调用者的扩展读写属性
fun callReflectProperty() {
    val clazz = ReflectClass::class
    val ins = clazz.createInstance()
    val props = clazz.declaredMemberProperties
    props.forEach {
        when (it.name) {
            "name" -> {
                @Suppress("UNCHECKED_CAST")
                // 将属性转换为读写属性
                val mp = it as KMutableProperty1<ReflectClass, Any>
                // 修改属性值
                mp.set(ins, "疯狂Java讲义")
                println(it.get(ins))
            }
            "age" -> {
                // 只读属性，只能通过get()方法读取属性值
                println(it.get(ins))
            }
        }
    }

   //用对象绑定属性和方法后，调用时无须传入调用者
    val str = "Kotlin"
    // 获取对象绑定的属性
    val prop = str::length
    // 调用绑定的属性时无需传入调用者
    println(prop.get())
    // 获取对象绑定的方法
    val f: (CharSequence, Boolean)->Boolean = str::endsWith
    // 调用绑定的方法时无需传入调用者
    println(f("lin" , true))
    var list = listOf("Kotlin", "Java", "Go", "Erlang")
    // 获取对象绑定的方法
    val fn = list::subList
    // 调用绑定的方法时无需传入调用者
    println(fn(1, 3)) // 输出["Java", "Go"]
    // 获取对象绑定的属性
    val prp = list::indices
    // 调用绑定的属性时无需传入调用者
    println(prp.get()) // 输出0..3
}

fun javaInteroperation() {
    //类
    val kotlinClazz=ReflectClass::class
    val javaClazz=kotlinClazz.java
    val anotherKotlinClazz=javaClazz.kotlin
    println(javaClazz.javaClass)
    //构造器
    val c=kotlinClazz.primaryConstructor
    val javaConstructor=c?.javaConstructor
    val o=javaConstructor?.newInstance(16)
    println("${o?.name}------${o?.age}")
    //函数
    val f = ReflectClass::test
    val javaMethod=f.javaMethod
    javaMethod?.invoke(ReflectClass(),"Java",55.1)
    //属性
    val topProp = ::topStr
    println(topProp.javaField?.get(o)) // 获取幕后字段
    println(topProp.javaGetter) // 获取getter方法
    println(topProp.javaSetter)	// 获取setter方法

    // 获取Item的name属性，属于KMutableProperty1的实例
    val mp = ReflectClass::name
    println(mp.javaField) // 获取幕后字段
    println(mp.javaGetter?.invoke(o)) // 获取getter方法
    println(mp.javaSetter?.invoke(o,"李四"))	// 获取setter方法
    println("${o?.name}------${o?.age}")

    // 获取Item的name属性，属于KProperty1的实例
    val prop = ReflectClass::age
    println(prop.javaField) // 获取幕后字段
    println(prop.javaGetter) // 获取getter方法
}