// Copyright (c) Shenyang Leading Edge Intelligent Technology Co., Ltd. All rights reserved.

package container

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

// 组件实例工厂
type Factory reflect.Value

// 设置字段值
// 参数
// 1.需要设置字段值的实例
// 2.可以设置的类型和值的映射
// 返回值:
// 1.错误
func (factory Factory) setReferences(instance reflect.Value, references map[reflect.Type]reflect.Value) error {

	implement := reflect.ValueOf(instance.Interface())
	implementPointerType := implement.Type()
	implementType := implementPointerType.Elem()
	for i := 0; i < implementType.NumField(); i++ {
		field := implementType.Field(i)
		if reference, ok := references[field.Type]; ok {
			methodName := fmt.Sprintf("Set%s", strings.Title(field.Name))
			method, ok := implementPointerType.MethodByName(methodName)
			if ok {
				methodType := method.Type // 从结构体指针类型获取方法类型,Receiver算作入参
				if methodType.NumIn() != 2 {
					return errors.New(fmt.Sprintf("设置器参数数量不正确!"))
				}
				if methodType.NumOut() != 0 {
					return errors.New(fmt.Sprintf("设置器返回值数量不正确!"))
				}
				if methodType.In(1) != field.Type {
					return errors.New(fmt.Sprintf("设置器类型不正确!"))
				}
				implement.MethodByName(methodName).Call([]reflect.Value{reference})
			}
		}
	}
	return nil
}

// 创建实例
// 参数
// 1.会话上下文
// 返回值:
// 1.调用器
// 2.错误
func (factory Factory) Create(context *SessionContext) (Instance, error) {

	if context == nil {
		return ZeroInstance, errors.New(fmt.Sprintf("会话上下文不能为空!"))
	}
	instanceValue := reflect.Value(factory).Call(emptyParameters)[0]
	actualValue := instanceValue
	if instanceValue.Kind() == reflect.Interface {
		actualValue = reflect.ValueOf(instanceValue.Interface())
	}
	// 检查空接口
	if actualValue.Kind() == reflect.Invalid {
		return ZeroInstance, errors.New(fmt.Sprintf("实际值不能是空接口!"))
	}
	if actualValue.Kind() != reflect.Ptr {
		return Instance(instanceValue), nil
	}
	// 检查空指针,首先要保证是指针
	if actualValue.IsNil() {
		return ZeroInstance, errors.New(fmt.Sprintf("实际值不能是空指针!"))
	}
	actualType := actualValue.Type()
	if actualType.Elem().Kind() != reflect.Struct {
		return Instance(instanceValue), nil
	}
	err := factory.setReferences(actualValue, map[reflect.Type]reflect.Value{sessionContextType: reflect.ValueOf(context)})
	if err != nil {
		return ZeroInstance, err
	}
	return Instance(instanceValue), nil
}
