// Copyright 2025 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

//go:build go1.24

package slhdsa

import "io"

const (
	maxN         = 32
	maxM         = 49
	maxK         = 35
	maxA         = 9
	maxKTimesA   = maxK * maxA
	maxWotsLen   = 2*maxN + 3

	maxContextLen = 255
)

type params struct {
	alg              string
	isShake          int
	isSM3            int
	n                uint32 // Security parameter (Hash output size in bytes) (16, 24, 32)
	len              uint32 // 2*n + 3
	h                uint32 // The total height of the tree (63, 64, 66, 68). #keypairs = 2^h
	d                uint32 // The number of tree layers (7, 8, 17, 22)
	hm               uint32 // The height (h') of each merkle tree. (h = hm * d )
	a                uint32 // Height of a FORS tree
	k                uint32 // The number of FORS trees
	m                uint32 // The size of H_MSG() output
	securityCategory uint32
	sigLen           int
	pkLen            int
}

// sigLen = (1+k*(1+a)+d*(hm+len))*n
var (
	SLHDSA128SmallSHA2 = params{alg: "SLH-DSA-SHA2-128s", isShake: 0, isSM3: 0, n: 16, len: 35, h: 63, d: 7, hm: 9, a: 12, k: 14, m: 30,
		securityCategory: 1, sigLen: 7856, pkLen: 32}
	SLHDSA128SmallSM3 = params{alg: "SLH-DSA-SM3-128s", isShake: 0, isSM3: 1, n: 16, len: 35, h: 63, d: 7, hm: 9, a: 12, k: 14, m: 30,
		securityCategory: 1, sigLen: 7856, pkLen: 32}
	SLHDSA128SmallSHAKE = params{alg: "SLH-DSA-SHAKE-128s", isShake: 1, isSM3: 0, n: 16, len: 35, h: 63, d: 7, hm: 9, a: 12, k: 14, m: 30,
		securityCategory: 1, sigLen: 7856, pkLen: 32}
	SLHDSA128FastSHA2 = params{alg: "SLH-DSA-SHA2-128f", isShake: 0, isSM3: 0, n: 16, len: 35, h: 66, d: 22, hm: 3, a: 6, k: 33, m: 34,
		securityCategory: 1, sigLen: 17088, pkLen: 32}
	SLHDSA128FastSM3 = params{alg: "SLH-DSA-SM3-128f", isShake: 0, isSM3: 10, n: 16, len: 35, h: 66, d: 22, hm: 3, a: 6, k: 33, m: 34,
		securityCategory: 1, sigLen: 17088, pkLen: 32}
	SLHDSA128FastSHAKE = params{alg: "SLH-DSA-SHAKE-128f", isShake: 1, isSM3: 0, n: 16, len: 35, h: 66, d: 22, hm: 3, a: 6, k: 33, m: 34,
		securityCategory: 1, sigLen: 17088, pkLen: 32}
	SLHDSA192SmallSHA2 = params{alg: "SLH-DSA-SHA2-192s", isShake: 0, isSM3: 0, n: 24, len: 51, h: 63, d: 7, hm: 9, a: 14, k: 17, m: 39,
		securityCategory: 3, sigLen: 16224, pkLen: 48}
	SLHDSA192SmallSHAKE = params{alg: "SLH-DSA-SHAKE-192s", isShake: 1, isSM3: 0, n: 24, len: 51, h: 63, d: 7, hm: 9, a: 14, k: 17, m: 39,
		securityCategory: 3, sigLen: 16224, pkLen: 48}
	SLHDSA192FastSHA2 = params{alg: "SLH-DSA-SHA2-192f", isShake: 0, isSM3: 0, n: 24, len: 51, h: 66, d: 22, hm: 3, a: 8, k: 33, m: 42,
		securityCategory: 3, sigLen: 35664, pkLen: 48}
	SLHDSA192FastSHAKE = params{alg: "SLH-DSA-SHAKE-192f", isShake: 1, isSM3: 0, n: 24, len: 51, h: 66, d: 22, hm: 3, a: 8, k: 33, m: 42,
		securityCategory: 3, sigLen: 35664, pkLen: 48}
	SLHDSA256SmallSHA2 = params{alg: "SLH-DSA-SHA2-256s", isShake: 0, isSM3: 0, n: 32, len: 67, h: 64, d: 8, hm: 8, a: 14, k: 22, m: 47,
		securityCategory: 5, sigLen: 29792, pkLen: 64}
	SLHDSA256SmallSHAKE = params{alg: "SLH-DSA-SHAKE-256s", isShake: 1, isSM3: 0, n: 32, len: 67, h: 64, d: 8, hm: 8, a: 14, k: 22, m: 47,
		securityCategory: 5, sigLen: 29792, pkLen: 64}
	SLHDSA256FastSHA2 = params{alg: "SLH-DSA-SHA2-256f", isShake: 0, isSM3: 0, n: 32, len: 67, h: 68, d: 17, hm: 4, a: 9, k: 35, m: 49,
		securityCategory: 5, sigLen: 49856, pkLen: 64}
	SLHDSA256FastSHAKE = params{alg: "SLH-DSA-SHAKE-256f", isShake: 1, isSM3: 0, n: 32, len: 67, h: 68, d: 17, hm: 4, a: 9, k: 35, m: 49,
		securityCategory: 5, sigLen: 49856, pkLen: 64}
)

var parameterSets = map[string]*params{
	"SLH-DSA-SHA2-128s":  &SLHDSA128SmallSHA2,
	"SLH-DSA-SHA2-128f":  &SLHDSA128FastSHA2,
	"SLH-DSA-SHA2-192s":  &SLHDSA192SmallSHA2,
	"SLH-DSA-SHA2-192f":  &SLHDSA192FastSHA2,
	"SLH-DSA-SHA2-256s":  &SLHDSA256SmallSHA2,
	"SLH-DSA-SHA2-256f":  &SLHDSA256FastSHA2,
	"SLH-DSA-SM3-128s":   &SLHDSA128SmallSM3,
	"SLH-DSA-SM3-128f":   &SLHDSA128FastSM3,
	"SLH-DSA-SHAKE-128s": &SLHDSA128SmallSHAKE,
	"SLH-DSA-SHAKE-128f": &SLHDSA128FastSHAKE,
	"SLH-DSA-SHAKE-192s": &SLHDSA192SmallSHAKE,
	"SLH-DSA-SHAKE-192f": &SLHDSA192FastSHAKE,
	"SLH-DSA-SHAKE-256s": &SLHDSA256SmallSHAKE,
	"SLH-DSA-SHAKE-256f": &SLHDSA256FastSHAKE,
}

func GetParameterSet(name string) (*params, bool) {
	if p, ok := parameterSets[name]; ok {
		return p, true
	}
	return nil, false
}

func (p *params) Equal(x any) bool {
	if x == nil {
		return false
	}
	if p2, ok := x.(*params); ok {
		return p.alg == p2.alg && p.isShake == p2.isShake && p.isSM3 == p2.isSM3 &&
			p.n == p2.n && p.h == p2.h && p.d == p2.d && p.hm == p2.hm &&
			p.a == p2.a && p.k == p2.k && p.m == p2.m &&
			p.securityCategory == p2.securityCategory &&
			p.sigLen == p2.sigLen && p.pkLen == p2.pkLen
	}
	return false
}

func (p *params) mdLen() int {
	return int(p.k*p.a+7) >> 3
}

func (p *params) treeIdxLen() int {
	return int(p.h-p.hm+7) >> 3 // 7 or 8 bytes
}

func (p *params) treeIdxMask() uint64 {
	return (1 << (p.h - p.hm)) - 1
}

func (p *params) leafIdxLen() int {
	return int(p.hm+7) >> 3 // 1 or 2 bytes
}

func (p *params) leafIdxMask() uint64 {
	return (1 << p.hm) - 1
}

func (p *params) String() string {
	return p.alg
}

// GenerateKey generates a new private key using the provided random source and the parameters
// specified by the receiver.
func (p *params) GenerateKey(rand io.Reader) (*PrivateKey, error) {
	return GenerateKey(rand, p)
}

// NewPublicKey creates a new PublicKey instance from the provided byte slice using the current parameter set.
func (p *params) NewPublicKey(bytes []byte) (*PublicKey, error) {
	return NewPublicKey(bytes, p)
}

// NewPrivateKey creates a new PrivateKey instance using the provided byte slice and the current parameter set.
func (p *params) NewPrivateKey(bytes []byte) (*PrivateKey, error) {
	return NewPrivateKey(bytes, p)
}
