package gameapi

import (
	"../log"
	"../mod"
	"../nt"
	pb "../protos"
	"../utils"
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"go.uber.org/zap"
	"io"
	rand2 "math/rand"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// =================== CBC ======================
func AesEncryptCBC(origData []byte, key []byte) (encrypted []byte) {
	// 分组秘钥
	// NewCipher该函数限制了输入k的长度必须为16, 24或者32
	block, _ := aes.NewCipher(key)
	blockSize := block.BlockSize()                              // 获取秘钥块的长度
	origData = pkcs5Padding(origData, blockSize)                // 补全码
	blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) // 加密模式
	encrypted = make([]byte, len(origData))                     // 创建数组
	blockMode.CryptBlocks(encrypted, origData)                  // 加密
	return encrypted
}

func AesDecryptCBC(encrypted []byte, key []byte) (decrypted []byte) {
	block, _ := aes.NewCipher(key)                              // 分组秘钥
	blockSize := block.BlockSize()                              // 获取秘钥块的长度
	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) // 加密模式
	decrypted = make([]byte, len(encrypted))                    // 创建数组
	blockMode.CryptBlocks(decrypted, encrypted)                 // 解密
	decrypted = pkcs5UnPadding(decrypted)                       // 去除补全码
	return decrypted
}
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)]
}

// =================== ECB ======================
func AesEncryptECB(origData []byte, key []byte) (encrypted []byte) {
	cipher, _ := aes.NewCipher(generateKey(key))
	length := (len(origData) + aes.BlockSize) / aes.BlockSize
	plain := make([]byte, length*aes.BlockSize)
	copy(plain, origData)
	pad := byte(len(plain) - len(origData))
	for i := len(origData); i < len(plain); i++ {
		plain[i] = pad
	}
	encrypted = make([]byte, len(plain))
	// 分组分块加密
	for bs, be := 0, cipher.BlockSize(); bs <= len(origData); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
		cipher.Encrypt(encrypted[bs:be], plain[bs:be])
	}

	return encrypted
}
func AesDecryptECB(encrypted []byte, key []byte) (decrypted []byte) {
	cipher, _ := aes.NewCipher(generateKey(key))
	decrypted = make([]byte, len(encrypted))
	//
	for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
		cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
	}

	trim := 0
	if len(decrypted) > 0 {
		trim = len(decrypted) - int(decrypted[len(decrypted)-1])
	}

	return decrypted[:trim]
}

func generateKey(key []byte) (genKey []byte) {
	genKey = make([]byte, 16)
	copy(genKey, key)
	for i := 16; i < len(key); {
		for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
			genKey[j] ^= key[i]
		}
	}
	return genKey
}

// =================== CFB ======================
func AesEncryptCFB(origData []byte, key []byte) (encrypted []byte) {
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}
	encrypted = make([]byte, aes.BlockSize+len(origData))
	iv := encrypted[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}
	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(encrypted[aes.BlockSize:], origData)
	return encrypted
}

func AesDecryptCFB(encrypted []byte, key []byte) (decrypted []byte) {
	block, _ := aes.NewCipher(key)
	if len(encrypted) < aes.BlockSize {
		panic("ciphertext too short")
	}
	iv := encrypted[:aes.BlockSize]
	encrypted = encrypted[aes.BlockSize:]

	stream := cipher.NewCFBDecrypter(block, iv)
	stream.XORKeyStream(encrypted, encrypted)
	return encrypted
}

func AesEncrypt(s string, desKey string) string {
	origData := []byte(s) // 待加密的数据
	key := []byte(desKey) // 加密的密钥
	//log.Println("原文:",string(s))

	//log.Println("------------------ CBC模式 --------------------")
	//encrypted := AesEncryptCBC(origData, key)
	//log.Println("密文(hex)：", hex.EncodeToString(encrypted))
	//log.Println("密文(base64)：", base64.StdEncoding.EncodeToString(encrypted))
	//decrypted := AesDecryptCBC(encrypted, key)
	//log.Println("解密结果：", string(decrypted))
	//
	//log.Println("------------------ ECB模式 --------------------")
	encrypted := AesEncryptECB(origData, key)
	//log.Println("密文(hex)：", hex.EncodeToString(encrypted))
	//log.Println("密文(base64)：", base64.StdEncoding.EncodeToString(encrypted))
	//R0GxW%2Bq8TMoEaREyWs20BSugPG%2BWfnAjz%2FeeNtNcaXLOhOH8ckTvF3v1BkwOBXUIUOFAlLiQ7ejVELetd0Oduw%3D%3D
	//str,_ := base64.StdEncoding.DecodeString("WzGO6nhlIVbsoqJDkswSyyy5KIVssnDXARlMPSzMpAjvTC6SGEzrn6zP0gLEEoBM")
	//R0GxW%2Bq8TMoEaREyWs20BSugPG%2BWfnAjz%2FeeNtNcaXIo9cQU2Lz5VYrCurgiCNx6uRT5kDjzZA9RyYtIIoKaIA%3D%3D
	//decryptedss := AesDecryptECB(str, key)
	//log.Println("解密结果:", string(decryptedss))
	//
	//log.Println("------------------ CFB模式 --------------------")
	//encrypted := AesEncryptCFB(origData, key)
	//log.Println("密文(hex)：", hex.EncodeToString(encrypted))
	//log.Println("密文(base64)：", base64.StdEncoding.EncodeToString(encrypted))
	//decrypted = AesDecryptCFB(encrypted, key)
	//log.Println("解密结果：", string(encrypted))

	//base64.URLEncoding.EncodeToString(encrypted)
	return base64.StdEncoding.EncodeToString(encrypted)
}

func Md5Encrypt(key string) string {
	data := []byte(key)
	has := md5.Sum(data)
	return fmt.Sprintf("%x", has)
}

func TimestampMillisecond() string {
	return strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
}

func GetFormatDate() string {
	strTime := time.Now().Format("20060102150405.000")
	str := strings.Split(strTime, ".")
	return str[0] + str[1]
}

//玩家强制关端 通知nats 玩家离开游戏
func Ntf_Nats_LeaveGameInfo(guid uint64, gameId int64) {

	moeny, err := mod.GetUserAttr(guid, "balance")
	if err != nil {
		log.Debug("Ntf_Nats_LeaveGameInfo GetUserAttr  balance err", zap.Error(err))
		return
	}
	Nats_Ntf_Balance(guid, gameId, moeny, false)
}

//结算并且通知入库
func SettlementAndNtfToNats(gn string, guid uint64, gameId int64, delta int64)  int64{
	new_balance, err := mod.AddUserBalance(guid, delta)
	if err != nil {
		log.Error(gn+" 更新redis金币 err", zap.Error(err), zap.Uint64("guid", guid), zap.Int64("delta", delta))
		return 0
	}
	log.Info(gn+" 更新redis金币 SUCCESS", zap.Uint64("guid", guid), zap.Int64("delta", delta), zap.Int64("newBlance", new_balance))
	Nats_Ntf_Balance(guid, gameId, strconv.FormatInt(new_balance,10), false)
	return new_balance
}

//玩家进入、退出游戏 将余额发到nats
func Nats_Ntf_Balance(guid uint64, gameId int64, money string, is_EnterGame bool) {
	//进入游戏时给PHP传递当前余额
	//离开游戏时给PHP传递当前余额
	//延时任务预先下分一次

	log.Info("NATS 入库",zap.Bool("enter",is_EnterGame), zap.Uint64("guid", guid), zap.Int64("gameId", gameId),zap.String("money", money))

	if is_EnterGame {
		nt.WriteUserMsg(guid, int32(pb.Event_USER_ENTER_GAME), "enter", utils.H{
			"guid":   guid,
			"gameId": gameId,
			"money":  money,
		})
	} else {
		nt.WriteUserMsg(guid, int32(pb.Event_USER_LEAVE_GAME), "leave", utils.H{
			"guid":   guid,
			"gameId": gameId,
			"money":  money,
		})
	}
}

func GenIpaddr() string {
	rand2.Seed(time.Now().Unix())
	ip := fmt.Sprintf("%d.%d.%d.%d", rand2.Intn(255), rand2.Intn(255), rand2.Intn(255), rand2.Intn(255))
	return ip
}

//查找进入游戏最低限额
//func FindGameConfBygTypemType(gameId int64, paramArr []string) (conf pb.GameItemEntry, has bool) {
func FindGameConfBygameId(gameId int64) (conf pb.GameItemEntry, has bool) {
	Games := nt.DynConf.Games
	for i := 0; i < len(Games); i++ {
		if Games[i].IsGame == 1 {

			if Games[i].Id == gameId {
					return *Games[i], true
			}
			//
			//if len(paramArr) == 2 && len(Games[i].GameParam) == 2 {
			//	if Games[i].Id == gameId {
			//		if Games[i].GameParam[0] == paramArr[0] && Games[i].GameParam[1] == paramArr[1] {
			//			return *Games[i], true
			//		}
			//	}
			//}
			//
			//if len(paramArr) == 1 && len(Games[i].GameParam) == 1 {
			//	if Games[i].Id == gameId {
			//		if Games[i].GameParam[0] == paramArr[0] {
			//			return *Games[i], true
			//		}
			//	}
			//}
		}
	}
	log.Error("FindGameConfBygameId not exsited", zap.Int64("gameId", gameId))
	return pb.GameItemEntry{}, false
}

func IsPhoneNumber(phoneNum string) bool{
	reg := `^1([38][0-9]|14[579]|5[^4]|16[6]|7[1-35-8]|9[189])\d{8}$`
	rgx := regexp.MustCompile(reg)
	return rgx.MatchString(phoneNum)
}