package main

import "C"
import (
	"bytes"
	"encoding/binary"
	"fmt"
	"golang.org/x/text/encoding/simplifiedchinese"
	"strconv"
	"sync"
	"time"
	"unsafe"
)

type KeysType struct {
	Handle map[string]interface{}
}

var KeysW sync.Mutex
var Keys sync.Map
var KeysMapId int

func GetKeysMapId() int {
	KeysW.Lock()
	KeysMapId++
	X := KeysMapId
	if X < 0 || X > 2147483647 {
		X = 1
		KeysMapId = 1
	}
	KeysW.Unlock()
	return X
}

//export CreateKeys
func CreateKeys() int {
	_Keys := &KeysType{Handle: make(map[string]interface{})}
	KeysContext := GetKeysMapId()
	Keys.Store(KeysContext, _Keys)
	return KeysContext
}

//export RemoveKeys
func RemoveKeys(KeysHandle int) {
	kk, e := Keys.Load(KeysHandle)
	if e {
		k := kk.(*KeysType)
		if k.Handle != nil {
			for one := range k.Handle {
				delete(k.Handle, one)
			}
		}
	}
	Keys.Delete(KeysHandle)
}

//export KeysDelete
func KeysDelete(KeysHandle int, name *C.char) {
	KeysW.Lock()
	defer KeysW.Unlock()
	kk, e := Keys.Load(KeysHandle)
	if e {
		k := kk.(*KeysType)
		if k.Handle != nil {
			delete(k.Handle, C.GoString(name))
		}
	}
}

//export KeysRead
func KeysRead(KeysHandle int, name *C.char, p *int) uintptr {
	*p = 0
	KeysW.Lock()
	defer KeysW.Unlock()
	kk, e := Keys.Load(KeysHandle)
	if e {
		k := kk.(*KeysType)
		if k == nil {
			return 0
		}
		s := k.Handle[C.GoString(name)]
		if s == nil {
			return 0
		}
		switch v := s.(type) {
		case []byte:
			*p = len(v)
			if len(s.([]byte)) < 1 {
				return 0
			}
			return uintptr(unsafe.Pointer(&v[0]))
		case string:
			if len(v) < 1 {
				return 0
			}
			var b bytes.Buffer
			b.WriteString(v)
			b.Write([]byte{0})
			o := b.Bytes()
			*p = len(v)
			return uintptr(unsafe.Pointer(&o[0]))
		case int64:
			*p = -8
			sb := Int64ToBytes(v)
			k.Handle[C.GoString(name)] = sb
			return uintptr(unsafe.Pointer(&sb[0]))
		case float64:
			*p = -8
			sb := Int64ToBytes(int64(v))
			k.Handle[C.GoString(name)] = sb
			return uintptr(unsafe.Pointer(&sb[0]))
		case int:
			*p = -4
			sb := IntToBytes(v)
			k.Handle[C.GoString(name)] = sb
			return uintptr(unsafe.Pointer(&sb[0]))
		}

	}
	return 0
}

//export KeysWrite
func KeysWrite(KeysHandle int, name *C.char, val uintptr, len int) {
	data := CStringToBytesMap(val, len)
	KeysW.Lock()
	defer KeysW.Unlock()
	a, e := Keys.Load(KeysHandle)
	if e == false {
		return
	}
	s := a.(*KeysType)
	s.Handle[C.GoString(name)] = data
}

var AuthCharUTF8 = func(string2 *C.char) string {
	return AuthGBKtoUTF8(C.GoString(string2))
}

//export KeysWriteFloat
func KeysWriteFloat(KeysHandle int, name *C.char, val float64) {
	KeysW.Lock()
	defer KeysW.Unlock()
	a, e := Keys.Load(KeysHandle)
	if e == false {
		return
	}
	s := a.(*KeysType)
	s.Handle[C.GoString(name)] = val
}

//export KeysReadFloat
func KeysReadFloat(KeysHandle int, name *C.char) float64 {
	KeysW.Lock()
	defer KeysW.Unlock()
	kk, e := Keys.Load(KeysHandle)
	if e {
		k := kk.(*KeysType)
		if k == nil {
			return 0
		}
		s := k.Handle[C.GoString(name)]
		if s == nil {
			return 0
		}
		switch r := s.(type) {
		case float64:
			return r
		case int:
			return float64(r)
		case int64:
			return float64(r)
		default:
			return 0
		}
	}
	return 0
}

//export KeysWriteLong
func KeysWriteLong(KeysHandle int, name *C.char, val int64) {
	KeysW.Lock()
	defer KeysW.Unlock()
	a, e := Keys.Load(KeysHandle)
	if e == false {
		return
	}
	s := a.(*KeysType)
	s.Handle[C.GoString(name)] = val
}

//export KeysReadLong
func KeysReadLong(KeysHandle int, name *C.char) int64 {
	KeysW.Lock()
	defer KeysW.Unlock()
	kk, e := Keys.Load(KeysHandle)
	if e {
		k := kk.(*KeysType)
		if k == nil {
			return 0
		}
		s := k.Handle[C.GoString(name)]
		if s == nil {
			return 0
		}
		switch r := s.(type) {
		case int64:
			return r
		case float64:
			return int64(r)
		case int:
			return int64(r)
		default:
			return 0
		}
	}
	return 0
}

//export KeysWriteInt
func KeysWriteInt(KeysHandle int, name *C.char, val int) {
	KeysW.Lock()
	defer KeysW.Unlock()
	a, e := Keys.Load(KeysHandle)
	if e == false {
		return
	}
	s := a.(*KeysType)
	s.Handle[C.GoString(name)] = val
}

//export KeysReadInt
func KeysReadInt(KeysHandle int, name *C.char) int {
	KeysW.Lock()
	defer KeysW.Unlock()
	kk, e := Keys.Load(KeysHandle)
	if e {
		k := kk.(*KeysType)
		if k == nil {
			return 0
		}
		s := k.Handle[C.GoString(name)]
		if s == nil {
			return 0
		}
		switch r := s.(type) {
		case int64:
			return int(r)
		case float64:
			return int(r)
		case int:
			return r
		default:
			return 0
		}
	}
	return 0
}

//export KeysEmpty
func KeysEmpty(KeysHandle int) {
	KeysW.Lock()
	defer KeysW.Unlock()
	a, e := Keys.Load(KeysHandle)
	if e == false {
		return
	}
	s := a.(*KeysType)
	if s.Handle != nil {
		for k, _ := range s.Handle {
			delete(s.Handle, k)
		}
	}
}

//export KeysGetCount
func KeysGetCount(KeysHandle int) int {
	KeysW.Lock()
	defer KeysW.Unlock()
	a, e := Keys.Load(KeysHandle)
	if e == false {
		return 0
	}
	s := a.(*KeysType)
	return len(s.Handle)
}

//export KeysGetJson
func KeysGetJson(KeysHandle int) uintptr {
	KeysW.Lock()
	defer KeysW.Unlock()
	a, e := Keys.Load(KeysHandle)
	if e == false {
		return 0
	}
	s := a.(*KeysType)
	var get = func(cc interface{}) string {
		switch cv := cc.(type) {
		case string:
			return "\"" + cv + "\""
		case time.Time:
			return "\"" + cv.Format("2006-01-02 15:04:05") + "\""
		case bool:
			if cv {
				return "true"
			}
			return "false"
		case []byte:
			r := "["
			for _, v := range cv {
				r += strconv.Itoa(int(v)) + ","
			}
			r = r[0:len(r)-1] + "]"
			return r
		case int:
			return strconv.Itoa(cv)
		case int8:
			return strconv.Itoa(int(cv))
		case int16:
			return strconv.Itoa(int(cv))
		case int32:
			return strconv.Itoa(int(cv))
		case int64:
			return strconv.FormatInt(cv, 10)
		case byte:
			return strconv.Itoa(int(cv))
		case uintptr:
			return strconv.Itoa(int(cv))
		case uint:
			return strconv.Itoa(int(cv))
		case uint16:
			return strconv.Itoa(int(cv))
		case uint32:
			return strconv.Itoa(int(cv))
		case uint64:
			return strconv.Itoa(int(cv))
		case float32:
			return strconv.FormatFloat(float64(cv), 'f', 6, 64)
		case float64:
			return strconv.FormatFloat(cv, 'f', 6, 64)
		default:
			return "\"类似不支持\""
		}
	}
	if s.Handle != nil {
		record := ""
		for k, v := range s.Handle {
			record += "\"" + k + "\":" + get(v) + ","
		}
		if len(record) > 0 {
			record = "{" + record[0:len(record)-1] + "}"
		} else {
			record = "{}"
		}
		b := []byte(record)
		c := C.CString(string(b))
		return uintptr(unsafe.Pointer(c))
	}
	return 0
}

//export KeysWriteStr
func KeysWriteStr(KeysHandle int, name *C.char, val uintptr, len int) {
	data := CStringToBytesMap(val, len)
	KeysW.Lock()
	defer KeysW.Unlock()
	a, e := Keys.Load(KeysHandle)
	if e == false {
		return
	}
	s := a.(*KeysType)
	s.Handle[C.GoString(name)] = string(data)
}

func AuthUtf8ToGbk(s string) string {
	b := []byte(s)
	if isUtf8(b) == false {
		return s
	}
	b1, e := simplifiedchinese.GBK.NewEncoder().Bytes(b) //utf-8 转 gbk
	if e != nil {
		return s
	}
	return string(b1)
}

var Int64ToBytes = func(data int64) []byte {
	bytebuf := bytes.NewBuffer([]byte{})
	binary.Write(bytebuf, binary.BigEndian, &data)
	ss := bytebuf.Bytes()
	return ss
}

var AuthGBKtoUTF8 = func(string2 string) string {
	b := []byte(string2)
	if isUtf8(b) == false {
		b1, e := simplifiedchinese.GBK.NewDecoder().Bytes(b) //gbk 转 utf-8
		if e != nil {
			return string2
		}
		return string(b1)
	}
	return string2
}

func isUtf8(data []byte) bool {
	for i := 0; i < len(data); {
		if data[i]&0x80 == 0x00 {
			// 0XXX_XXXX
			i++
			continue
		} else if num := preNUm(data[i]); num > 2 {
			// 110X_XXXX 10XX_XXXX
			// 1110_XXXX 10XX_XXXX 10XX_XXXX
			// 1111_0XXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
			// 1111_10XX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
			// 1111_110X 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
			// preNUm() 返回首个字节的8个bits中首个0bit前面1bit的个数，该数量也是该字符所使用的字节数
			i++
			for j := 0; j < num-1; j++ {
				//判断后面的 num - 1 个字节是不是都是10开头
				if data[i]&0xc0 != 0x80 {
					return false
				}
				i++
			}
		} else {
			//其他情况说明不是utf-8
			return false
		}
	}
	return true
}
func preNUm(data byte) int {
	str := fmt.Sprintf("%b", data)
	var i int = 0
	for i < len(str) {
		if str[i] != '1' {
			break
		}
		i++
	}
	return i
}

var CStringToBytesMap = func(r uintptr, datalen int) []byte {
	data := make([]byte, 0)
	for i := 0; i < datalen; i++ {
		data = append(data, *(*byte)(unsafe.Pointer(r + uintptr(i))))
	}
	return data
}
