/*
	some common api for change string between int/32/64,uint8/16/32/64,float32/64
*/
package common

import (
	pb "World/pb"
	//pmgr "World/playermgr"
	"bytes"
	"common/beegomap"
	"common/logger"
	"encoding/binary"
	"fmt"
	"math/rand"
	"strconv"
	"strings"
	"time"
	"unicode"

	"github.com/golang/protobuf/proto"
)

const (
	alphanum     = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	sub_alphanum = "FLGW5XC39ZM67YRT2HS8DVEJ4KQPUANB"
	binLen       = 32
	startNumber  = 33554432

	startNumber8  = 34359738368
)

var (
	PlayerMap *beegomap.BeeMap //  = make(map[int]int)
)

func init() {
	PlayerMap = beegomap.NewBeeMap()
}

func Atoi(s string) int {
	if s == "" {
		return 0
	}

	i, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return i
}

func Atouint(s string) uint {
	return uint(Atoi(s))
}

func Atoi64(s string) int64 {
	if s == "" {
		return 0
	}

	i, err := strconv.ParseInt(s, 10, 0)

	if err != nil {
		panic(err)
	}
	return i
}

func Atof32(s string) float32 {
	f, err := strconv.ParseFloat(s, 32)
	if err != nil {
		panic(err)
	}
	return float32(f)
}

func Atof64(s string) float64 {
	f, err := strconv.ParseFloat(s, 64)
	if err != nil {
		panic(err)
	}
	return f
}

func Atoi32(s string) int32 {
	i, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return int32(i)
}

func Atobyte(s string) byte {
	i, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return byte(i)
}

func Itoa(n int) string {
	s := strconv.Itoa(n)
	return s
}

func I32toa(n int32) string {
	return fmt.Sprintf("%d", n)
}

func I64toa(i int64) (s string) {
	s = fmt.Sprintf("%d", i)
	return s
}

func U32toa(n uint32) string {
	return fmt.Sprintf("%d", n)
}

func String2IntArray(s string) (ar []int) {
	for _, vId := range strings.Split(s, ",") {
		ar = append(ar, Atoi(vId))
	}
	return ar
}

func IntArray2String(ar []int) string {
	vs := []string{}
	for _, a := range ar {
		vs = append(vs, I64toa(int64(a)))
	}
	return strings.Join(vs, ",")
}

func String2Int32Array(s string) (ar []int32) {
	for _, vId := range strings.Split(s, ",") {
		ar = append(ar, Atoi32(vId))
	}
	return ar
}

func Int32Array2String(ar []int32) string {
	vs := []string{}
	for _, a := range ar {
		vs = append(vs, I64toa(int64(a)))
	}
	return strings.Join(vs, ",")
}

func String2Int64Array(s string) (ar []int64) {
	for _, vId := range strings.Split(s, ",") {
		ar = append(ar, Atoi64(vId))
	}
	return ar
}

func StringArray2Int64Array(ss []string) (ar []int64) {
	ar = make([]int64, len(ss))
	for index, s := range ss {
		ar[index] = Atoi64(s)
	}
	return
}

func Int64Array2String(ar []int64) string {
	vs := []string{}
	for _, a := range ar {
		vs = append(vs, I64toa(a))
	}
	return strings.Join(vs, ",")
}

// Int2Fuzzy 3 -> (?,?,?)
func Int2Fuzzy(ir int) string {
	fuzAr := make([]string, ir)
	for i := 0; i < ir; i++ {
		fuzAr[i] = "?"
	}
	return "(" + strings.Join(fuzAr, ",") + ")"
}

func AInt2AInt64(ar []int) (br []int64) {
	br = make([]int64, len(ar))
	for index, r := range ar {
		br[index] = int64(r)
	}
	return
}

func UnderScore2Calm(s string) string {
	words := strings.Split(s, "_")
	var newWords []string

	for _, word := range words {
		a := []rune(word)
		a[0] = unicode.ToUpper(a[0])
		newWords = append(newWords, string(a))
	}

	return strings.Join(newWords, "")
}

func Calm2UnderScore(s string) string {
	var words []string
	for i := 0; s != ""; s = s[i:] {
		i = strings.IndexFunc(s[1:], unicode.IsUpper) + 1
		if i <= 0 {
			i = len(s)
		}
		words = append(words, strings.ToLower(s[:i]))
	}
	return strings.Join(words, "_")
}

func Atob(str string) bool {
	if str == "1" {
		return true
	}

	return false
}

func Atoas(str string) []string {
	return strings.Split(str, "|")
}

func Atoi64s(str string) []int64 {
	as := strings.Split(str, "|")
	is := make([]int64, len(as))

	for i, v := range as {
		is[i] = Atoi64(v)
	}

	return is
}

func DownFirstChar(s string) string {
	cs := []rune(s)
	cs[0] = unicode.ToLower(cs[0])
	return string(cs)
}

func Singular(str string) string {
	if strings.HasSuffix(str, "es") {
		return str[0 : len(str)-2]
	}

	if strings.HasSuffix(str, "s") {
		return str[0 : len(str)-1]
	}

	return str
}

func GetCurrentUinxTimeStamp() string {
	ts := time.Now().Unix()
	stamp := fmt.Sprint(ts)
	return stamp
}

func Inet_addr(ipaddr string) uint32 {
	var (
		segments []string = strings.Split(ipaddr, ".")
		ip       [4]uint64
		ret      uint64
	)

	if len(segments) != 4 {
		return 0
	}

	for i := 0; i < 4; i++ {
		ip[i], _ = strconv.ParseUint(segments[i], 10, 64)
	}
	ret = ip[3]<<24 + ip[2]<<16 + ip[1]<<8 + ip[0]
	return uint32(ret)
}

func Inet_ntoa(ipnr uint32) string {
	var bytes [4]byte
	bytes[0] = byte(ipnr & 0xFF)
	bytes[1] = byte((ipnr >> 8) & 0xFF)
	bytes[2] = byte((ipnr >> 16) & 0xFF)
	bytes[3] = byte((ipnr >> 24) & 0xFF)

	return fmt.Sprintf("%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3])
}

func Inet_aton(ipnr string) uint32 {
	bits := strings.Split(ipnr, ".")

	b0, _ := strconv.Atoi(bits[0])
	b1, _ := strconv.Atoi(bits[1])
	b2, _ := strconv.Atoi(bits[2])
	b3, _ := strconv.Atoi(bits[3])

	var sum uint32

	sum += uint32(b3) << 24
	sum += uint32(b2) << 16
	sum += uint32(b1) << 8
	sum += uint32(b0)

	return sum
}

func GetRandomString(n uint32) string {
	rand.Seed(time.Now().UnixNano())
	b := make([]byte, n)
	for i := range b {
		b[i] = alphanum[rand.Intn(len(alphanum))]
	}
	return string(b)
}

func IsIdExist(Ids []uint32, id uint32) bool {
	bExist := false

	for _, Id := range Ids {
		if Id == id {
			bExist = true
			break
		}
	}
	return bExist
}

func RemoveDuplicates(elements []uint32) []uint32 {
	encountered := map[uint32]bool{}
	result := []uint32{}

	for v := range elements {
		if encountered[elements[v]] == true {
		} else {
			encountered[elements[v]] = true
			result = append(result, elements[v])
		}
	}
	return result
}

func RemoveDuplicateIds(elements []int32) []int32 {
	encountered := map[int32]bool{}
	result := []int32{}

	for v := range elements {
		if encountered[elements[v]] == true {
		} else {
			encountered[elements[v]] = true
			result = append(result, elements[v])
		}
	}
	return result
}

func IsPlayerInPlayerMap(uid int) bool {
	return PlayerMap.Check(uint32(uid))
}

func SendNoti(player *GamePlayer, roomid uint32, toid uint32, msgid pb.MSGID, msg interface{}) bool {
	if player == nil {
		logger.Notic("send noti failed with nil conn, playerid:%v  msgid: %v", toid, msgid)
		return false
	}
	if !IsPlayerInPlayerMap(player.UID) {
		return false
	}

	tmp, err := proto.Marshal(msg.(proto.Message))
	if err != nil {
		logger.Notic("marshal msgid:%v  response body error:%v", msgid, err)
		return false
	}

	total_len := len(tmp) + int(Min_Message_Size)
	bys := new(bytes.Buffer)
	binary.Write(bys, binary.BigEndian, uint16(total_len))
	binary.Write(bys, binary.BigEndian, uint16(msgid))
	binary.Write(bys, binary.BigEndian, uint32(0))
	binary.Write(bys, binary.BigEndian, toid)
	binary.Write(bys, binary.BigEndian, roomid)
	buf := append(bys.Bytes(), tmp...)

	select {
	case player.OutChannel <- buf:
		break
	case <-time.After(time.Millisecond * 20):
		logger.Notic("send to uid:%v roomid:%v timeout, channel full", toid, roomid)
		return false
	}

	return true
}

func IdToCode(ori_id uint32) string {

	converted_id := ori_id + startNumber

	//charPos := 32
	var buf string

	for (converted_id / binLen) > 0 {
		ind := (int)(converted_id % binLen)
		//charPos--
		buf = string(sub_alphanum[ind]) + buf
		converted_id /= binLen

	}
	//charPos--

	buf = string(sub_alphanum[converted_id%binLen]) + buf

	return buf
}

func IdTo8BitCode(ori_id uint32) string {

	var converted_id uint64 = uint64(ori_id) + startNumber8

	var buf string

	for (converted_id / binLen) > 0 {
		ind := (int)(converted_id % binLen)
		buf = string(sub_alphanum[ind]) + buf
		converted_id /= binLen
	}

	buf = string(sub_alphanum[converted_id%binLen]) + buf

	return buf
}

func CodeToId(code string) uint32 {
	var res uint32 = 0
	for i := 0; i < len(code); i++ {
		var ind uint32 = 0
		for j := 0; j < binLen; j++ {
			if code[i] == sub_alphanum[j] {
				ind = uint32(j)
				break
			}
		}
		if i > 0 {
			res = res*binLen + ind
		} else {
			res = ind
		}
	}
	res -= startNumber
	return res
}

func Code8ToId(code string) uint32 {
	var res uint64 = 0
	for i := 0; i < len(code); i++ {
		var ind uint64 = 0
		for j := 0; j < binLen; j++ {
			if code[i] == sub_alphanum[j] {
				ind = uint64(j)
				break
			}
		}
		if i > 0 {
			res = res*binLen + ind
		} else {
			res = ind
		}
	}
	res -= startNumber8
	return uint32(res)
}

func SendGoldNumNotify(player *GamePlayer, roomId uint32, toid uint32, goldNum int64, changeNum int64) {
	//发送金币变动通知
	var noti pb.NoticeNotifyUserGoldNum
	noti.Uid = toid
	noti.GoldNum = goldNum
	noti.ChangeNum = changeNum
	SendNoti(player, roomId, toid, pb.MSGID_MsgID_NotifyUserGoldNum_Notice, &noti)
	logger.Info("send NoticeNotifyUserGoldNum notify uid:%v goldnum:%v", noti.Uid, noti.GoldNum)
}
