// Code generated by csid-generate.go DO NOT EDIT.

package {{.PkgName}}

import "crypto/rand"
import "crypto/sha256"
import "fmt"
import "github.com/go-playground/validator/v10"
import "github.com/rs/zerolog/log"
import "gogs.mikescher.com/BlackForestBytes/goext/exerr"
import "gogs.mikescher.com/BlackForestBytes/goext/langext"
import "gogs.mikescher.com/BlackForestBytes/goext/rext"
import "math/big"
import "reflect"
import "regexp"
import "strings"

const ChecksumCharsetIDGenerator = "{{.Checksum}}" // GoExtVersion: {{.GoextVersion}}

const idlen = 24

const checklen = 1

const idCharset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
const idCharsetLen = len(idCharset)

var charSetReverseMap = generateCharsetMap()

const ({{range .IDs}}
	prefix{{.Name}} = "{{.Prefix}}" {{end}}
)

var ({{range .IDs}}
	regex{{.Name}} = generateRegex(prefix{{.Name}}) {{end}}
)

func generateRegex(prefix string) rext.Regex {
	return rext.W(regexp.MustCompile(fmt.Sprintf("^%s[%s]{%d}[%s]{%d}$", prefix, idCharset, idlen-len(prefix)-checklen, idCharset, checklen)))
}

func generateCharsetMap() []int {
	result := make([]int, 128)
	for i := 0; i < len(result); i++ {
		result[i] = -1
	}
	for idx, chr := range idCharset {
		result[int(chr)] = idx
	}
	return result
}

func generateID(prefix string) string {
	k := ""
	csMax := big.NewInt(int64(idCharsetLen))
	checksum := 0
	for i := 0; i < idlen-len(prefix)-checklen; i++ {
		v, err := rand.Int(rand.Reader, csMax)
		if err != nil {
			panic(err)
		}
		v64 := v.Int64()
		k += string(idCharset[v64])
		checksum = (checksum + int(v64)) % (idCharsetLen)
	}
	checkstr := string(idCharset[checksum%idCharsetLen])
	return prefix + k + checkstr
}

func generateIDFromSeed(prefix string, seed string) string {
	h := sha256.New()

	iddata := ""
	for len(iddata) < idlen-len(prefix)-checklen {
		h.Write([]byte(seed))
		bs := h.Sum(nil)
		iddata += langext.NewAnyBaseConverter(idCharset).Encode(bs)
	}

	checksum := 0
	for i := 0; i < idlen-len(prefix)-checklen; i++ {
		ichr := int(iddata[i])
		checksum = (checksum + charSetReverseMap[ichr]) % (idCharsetLen)
	}

	checkstr := string(idCharset[checksum%idCharsetLen])

	return prefix + iddata[:(idlen-len(prefix)-checklen)] + checkstr
}

func validateID(prefix string, value string) error {
	if len(value) != idlen {
		return exerr.New(exerr.TypeInvalidCSID, "id has the wrong length").Str("value", value).Build()
	}

	if !strings.HasPrefix(value, prefix) {
		return exerr.New(exerr.TypeInvalidCSID, "id is missing the correct prefix").Str("value", value).Str("prefix", prefix).Build()
	}

	checksum := 0
	for i := len(prefix); i < len(value)-checklen; i++ {
		ichr := int(value[i])
		if ichr < 0 || ichr >= len(charSetReverseMap) || charSetReverseMap[ichr] == -1 {
			return exerr.New(exerr.TypeInvalidCSID, "id contains invalid characters").Str("value", value).Build()
		}
		checksum = (checksum + charSetReverseMap[ichr]) % (idCharsetLen)
	}

	checkstr := string(idCharset[checksum%idCharsetLen])

	if !strings.HasSuffix(value, checkstr) {
		return exerr.New(exerr.TypeInvalidCSID, "id checkstring is invalid").Str("value", value).Str("checkstr", checkstr).Build()
	}

	return nil
}

func getRawData(prefix string, value string) string {
	if len(value) != idlen {
		return ""
	}
	return value[len(prefix) : idlen-checklen]
}

func getCheckString(prefix string, value string) string {
	if len(value) != idlen {
		return ""
	}
	return value[idlen-checklen:]
}

func ValidateEntityID(vfl validator.FieldLevel) bool {
	if !vfl.Field().CanInterface() {
		log.Error().Msgf("Failed to validate EntityID (cannot interface ?!?)")
		return false
	}

	ifvalue := vfl.Field().Interface()

	if value1, ok := ifvalue.(EntityID); ok {

		if vfl.Field().Type().Kind() == reflect.Pointer && langext.IsNil(value1) {
			return true
		}

		if err := value1.Valid(); err != nil {
			log.Debug().Msgf("Failed to validate EntityID '%s' (%s)", value1.String(), err.Error())
			return false
		} else {
			return true
		}

	} else {
		log.Error().Msgf("Failed to validate EntityID (wrong type: %T)", ifvalue)
		return false
	}
}

{{range .IDs}}

// ================================ {{.Name}} ({{.FileRelative}}) ================================

func New{{.Name}}() {{.Name}} {
	return {{.Name}}(generateID(prefix{{.Name}}))
}

func (id {{.Name}}) Valid() error {
	return validateID(prefix{{.Name}}, string(id))
}

func (i {{.Name}}) String() string {
	return string(i)
}

func (i {{.Name}}) Prefix() string {
	return prefix{{.Name}}
}

func (id {{.Name}}) Raw() string {
	return getRawData(prefix{{.Name}}, string(id))
}

func (id {{.Name}}) CheckString() string {
	return getCheckString(prefix{{.Name}}, string(id))
}

func (id {{.Name}}) Regex() rext.Regex {
	return regex{{.Name}}
}

{{end}}