package models

import (
	"encoding/binary"
	"fmt"
	"hash"
	"hash/fnv"
	"math"
	"unsafe"
)

// fingerprint is a wrapper for hash.Hash64 that adds utility methods to simplify hash calculation of structs
type fingerprint struct {
	h hash.Hash64
}

// creates a fingerprint that is backed by 64bit FNV-1a hash
func newFingerprint() fingerprint {
	return fingerprint{h: fnv.New64a()}
}

func (f fingerprint) String() string {
	return fmt.Sprintf("%016x", f.h.Sum64())
}

func (f fingerprint) writeBytes(b []byte) {
	_, _ = f.h.Write(b)
	// add a byte sequence that cannot happen in UTF-8 strings.
	_, _ = f.h.Write([]byte{255})
}

func (f fingerprint) writeString(s string) {
	if len(s) == 0 {
		f.writeBytes(nil)
		return
	}
	// #nosec G103
	// avoid allocation when converting string to byte slice
	f.writeBytes(unsafe.Slice(unsafe.StringData(s), len(s)))
}

func (f fingerprint) writeFloat64(num float64) {
	bits := math.Float64bits(num)
	bytes := make([]byte, 8)
	binary.LittleEndian.PutUint64(bytes, bits)
	f.writeBytes(bytes)
}

func (f fingerprint) writeBool(b bool) {
	if b {
		f.writeBytes([]byte{1})
	} else {
		f.writeBytes([]byte{0})
	}
}
