package mapstructure

import (
	"reflect"
	"time"
)

// TimeToStringHook 时间转字符串
// 支持 *Time.time 转 string/*string
// 不能用 Time.time 转，它会在上层认为是一个结构体数据而直接转成map，再到hook方法
func TimeToStringHook(layout string) DecodeHookFunc {
	return func(
		f reflect.Type,
		t reflect.Type,
		data interface{}) (interface{}, error) {
		// 判断目标类型是否为字符串
		var strType string
		var isStrPointer *bool // 要转换的目标类型是否为指针字符串
		if t == reflect.TypeOf(strType) {
			isStrPointer = new(bool)
		} else if t == reflect.TypeOf(&strType) {
			isStrPointer = new(bool)
			*isStrPointer = true
		}
		if isStrPointer == nil {
			return data, nil
		}

		// 判断类型是否为时间
		timeType := time.Time{}
		if f != reflect.TypeOf(timeType) && f != reflect.TypeOf(&timeType) {
			return data, nil
		}

		// 将时间转换为字符串
		var output string
		switch v := data.(type) {
		case *time.Time:
			output = v.Format(layout)
		case time.Time:
			output = v.Format(layout)
		default:
			return data, nil
		}

		if *isStrPointer {
			return &output, nil
		}
		return output, nil
	}
}

// TimeToUnixIntHook 时间转时间戳
// 支持 *Time.time 转 uint/uint32/uint64/int/int32/int64，支持带指针
// 不能用 Time.time 转，它会在上层认为是一个结构体数据而直接转成map，再到hook方法
func TimeToUnixIntHook() DecodeHookFunc {
	return func(
		f reflect.Type,
		t reflect.Type,
		data interface{}) (interface{}, error) {

		tkd := t.Kind()
		if tkd != reflect.Int && tkd != reflect.Int32 && tkd != reflect.Int64 &&
			tkd != reflect.Uint && tkd != reflect.Uint32 && tkd != reflect.Uint64 {
			return data, nil
		}

		// 判断类型是否为时间
		timeType := time.Time{}
		if f != reflect.TypeOf(timeType) && f != reflect.TypeOf(&timeType) {
			return data, nil
		}

		// 将时间转换为字符串
		var output int64
		switch v := data.(type) {
		case *time.Time:
			output = v.Unix()
		case time.Time:
			output = v.Unix()
		default:
			return data, nil
		}
		switch tkd {
		case reflect.Int:
			return int(output), nil
		case reflect.Int32:
			return int32(output), nil
		case reflect.Int64:
			return output, nil
		case reflect.Uint:
			return uint(output), nil
		case reflect.Uint32:
			return uint32(output), nil
		case reflect.Uint64:
			return uint64(output), nil
		default:
			return data, nil
		}
	}
}
