package utils

import (
	"bufio"
	"bytes"
	"crypto/cipher"
	"crypto/des"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego/logs"
	"log"
	"os"
	"reflect"
	"regexp"
	"sort"
	"strconv"
)

/**
  struct 结构体属性之间复制
*/
func StructCopy(DstStructPtr interface{}, SrcStructPtr interface{}) {
	srcv := reflect.ValueOf(SrcStructPtr)
	dstv := reflect.ValueOf(DstStructPtr)
	srct := reflect.TypeOf(SrcStructPtr)
	dstt := reflect.TypeOf(DstStructPtr)
	if srct.Kind() != reflect.Ptr || dstt.Kind() != reflect.Ptr ||
		srct.Elem().Kind() == reflect.Ptr || dstt.Elem().Kind() == reflect.Ptr {
		panic("Fatal error:type of parameters must be Ptr of value")
	}
	if srcv.IsNil() || dstv.IsNil() {
		panic("Fatal error:value of parameters should not be nil")
	}
	srcV := srcv.Elem()
	dstV := dstv.Elem()
	srcfields := DeepFields(reflect.ValueOf(SrcStructPtr).Elem().Type())
	for _, v := range srcfields {
		if v.Anonymous {
			continue
		}
		dst := dstV.FieldByName(v.Name)
		src := srcV.FieldByName(v.Name)
		if !dst.IsValid() {
			continue
		}
		if src.Type() == dst.Type() && dst.CanSet() {
			dst.Set(src)
			continue
		}
		if src.Kind() == reflect.Ptr && !src.IsNil() && src.Type().Elem() == dst.Type() {
			dst.Set(src.Elem())
			continue
		}
		if dst.Kind() == reflect.Ptr && dst.Type().Elem() == src.Type() {
			dst.Set(reflect.New(src.Type()))
			dst.Elem().Set(src)
			continue
		}
	}
	return
}

func DeepFields(ifaceType reflect.Type) []reflect.StructField {
	var fields []reflect.StructField

	for i := 0; i < ifaceType.NumField(); i++ {
		v := ifaceType.Field(i)
		if v.Anonymous && v.Type.Kind() == reflect.Struct {
			fields = append(fields, DeepFields(v.Type)...)
		} else {
			fields = append(fields, v)
		}
	}
	return fields
}

/***
  struct 转成 json
*/
func TStructToJson(obj interface{}) string {
	var result = ""
	bytes, err := json.Marshal(obj)
	if err != nil {
		log.Println("struct 转成 json 失败 ！")
		return result
	}
	return string(bytes)
}

/**
  json 转成 struct
*/
func JsonToStruct(strJson string, obj interface{}) interface{} {

	json.Unmarshal([]byte(strJson), &obj)

	return obj
}

// 判断是否包含某个元素 v是否包含val
func IsContains(v interface{}, val interface{}) bool {
	switch reflect.TypeOf(v).Kind() {
	case reflect.Slice:
		{
			s := reflect.ValueOf(v)
			for i := 0; i < s.Len(); i++ {
				if reflect.DeepEqual(val, s.Index(i).Interface()) {
					return true
				}
			}
		}
	}
	return false
}

/**
int32转string
*/
func Int32ToString(n int32) string {
	buf := [11]byte{}
	pos := len(buf)
	i := int64(n)
	signed := i < 0
	if signed {
		i = -i
	}
	for {
		pos--
		buf[pos], i = '0'+byte(i%10), i/10
		if i == 0 {
			if signed {
				pos--
				buf[pos] = '-'
			}
			return string(buf[pos:])
		}
	}
}

func IsNil(i interface{}) bool {
	vi := reflect.ValueOf(i)
	if vi.Kind() == reflect.Ptr {
		return vi.IsNil()
	}
	return false
}

//去除字符串中的ansi字符
func ReplaceAnsiCharOfString(str string) string {
	const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
	reg, _ := regexp.Compile(ansi)
	return reg.ReplaceAllString(str, "")
}

//string数组去重
func RemoveDuplicatesAndEmpty(a []string) (ret []string) {
	sort.Strings(a)
	a_len := len(a)
	for i := 0; i < a_len; i++ {
		if (i > 0 && a[i-1] == a[i]) || len(a[i]) == 0 {
			continue
		}
		ret = append(ret, a[i])
	}
	return
}

/* 去除slice中重复的元素
 * params:
 *   s: slice对象，如[]string, []int, []float64, ...
 *      slice类型的指针
 * return:
 *   []interface{}: 已经去除重复元素的新的slice对象
 */
func RemoveSliceDuplicate(s interface{}) (result []interface{}) {
	t := reflect.TypeOf(s)
	va := reflect.ValueOf(s)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
		va = va.Elem()
	}
	if t.Kind() != reflect.Slice {
		errorLogger.Printf("<RemoveSliceDuplicate> <s> is not slice but %T\n", s)
		return result
	}

	for i := 0; i < va.Len(); i++ {
		state := false
		for j := i + 1; j < va.Len(); j++ {
			if j > 0 && reflect.DeepEqual(va.Index(i).Interface(), va.Index(j).Interface()) {
				state = true
				break
			}
		}
		if !state {
			result = append(result, va.Index(i).Interface())
		}
	}
	return result
}

//des的CBC加密
func EncryptDES_CBC(src string) (string, error) {
	//key最长8位
	const key = "K2pass!!"
	data := []byte(src)
	keyByte := []byte(key)
	block, err := des.NewCipher(keyByte)
	if err != nil {
		//panic(err)
		errorLogger.Println("加密失败:", err.Error())
		return "", err
	}
	data = PKCS5Padding(data, block.BlockSize())
	//获取CBC加密模式
	//用密钥作为向量(不建议这样使用)
	iv := keyByte
	mode := cipher.NewCBCEncrypter(block, iv)
	out := make([]byte, len(data))
	mode.CryptBlocks(out, data)
	return fmt.Sprintf("%X", out), nil
}

//des的CBC解密
func DecryptDES_CBC(src string) (string, error) {
	//key最长8位
	const key = "K2pass!!"
	keyByte := []byte(key)
	data, err := hex.DecodeString(src)
	if err != nil {
		//panic(err)
		errorLogger.Println("解密失败:", err.Error())
		return "", err
	}
	block, err := des.NewCipher(keyByte)
	if err != nil {
		//panic(err)
		errorLogger.Println("解密失败:", err.Error())
		return "", err
	}
	//用密钥作为向量(不建议这样使用)
	iv := keyByte
	mode := cipher.NewCBCDecrypter(block, iv)
	plaintext := make([]byte, len(data))
	mode.CryptBlocks(plaintext, data)
	plaintext = PKCS5UnPadding(plaintext)
	return string(plaintext), nil
}

//明文补码算法
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

//明文减码算法
func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

// 用golang自带的Scanner逐行读取文件，并返回string数组
func ReadFile(fileName string) (content []string, err error) {
	ret := make([]string, 0)
	file, err := os.Open(fileName)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)

	for scanner.Scan() {
		ret = append(ret, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}
	return ret, nil
}

/**
  判断字符串是否全数字
*/
func IsNumber(str string) bool {
	result, err := strconv.Atoi(str)
	logs.Info("判断字符串是否全是数字结果：", result)
	if err != nil {
		return false
	}
	return true
}
