package langext

import (
	"crypto/rand"
	"errors"
	"math"
	"math/big"
	"strings"
)

var (
	base62Base         = uint64(62)
	base62CharacterSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
)

func RandBase62(rlen int) string {
	bi52 := big.NewInt(int64(len(base62CharacterSet)))

	randMax := big.NewInt(math.MaxInt64)

	r := ""

	for i := 0; i < rlen; i++ {
		v, err := rand.Int(rand.Reader, randMax)
		if err != nil {
			panic(err)
		}

		r += string(base62CharacterSet[v.Mod(v, bi52).Int64()])
	}

	return r
}

func EncodeBase62(num uint64) string {
	if num == 0 {
		return "0"
	}

	b := make([]byte, 0)

	// loop as long the num is bigger than zero
	for num > 0 {
		r := num % base62Base

		num -= r
		num /= base62Base

		b = append([]byte{base62CharacterSet[int(r)]}, b...)
	}

	return string(b)
}

func DecodeBase62(str string) (uint64, error) {
	if str == "" {
		return 0, errors.New("empty string")
	}

	result := uint64(0)

	for _, v := range str {
		result *= base62Base

		pos := strings.IndexRune(base62CharacterSet, v)
		if pos == -1 {
			return 0, errors.New("invalid character: " + string(v))
		}

		result += uint64(pos)
	}

	return result, nil
}