// KEKS/CM -- KEKS-encoded cryptographic messages
// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program.  If not, see <http://www.gnu.org/licenses/>.

package hash

import (
	"hash"
	"runtime"

	"go.cypherpunks.su/gogost/v6/gost34112012256"
	"go.cypherpunks.su/gogost/v6/gost34112012512"
	"golang.org/x/crypto/blake2b"

	cmblake2b "go.cypherpunks.su/keks/cm/hash/blake2b"
	"go.cypherpunks.su/keks/cm/hash/gost"
	"go.cypherpunks.su/keks/cm/hash/merkle"
)

const (
	BLAKE2b     = "blake2b"
	BLAKE2b256  = "blake2b256"
	SHA2512     = "sha2-512"
	SHAKE128    = "shake128"
	SHAKE256    = "shake256"
	Streebog256 = "streebog256"
	Streebog512 = "streebog512"

	BLAKE2bMerkle     = "blake2b-merkle"
	Streebog256Merkle = "streebog256-merkle"
	Streebog512Merkle = "streebog512-merkle"
)

var DefaultNumCPU = max(1, runtime.NumCPU()/2)

func ByName(name string) hash.Hash {
	switch name {
	case Streebog256, gost.GOST3410256A:
		return gost34112012256.New()
	case Streebog256Merkle, gost.GOST3410256AMerkle:
		return gost.NewMerkleHasher(gost34112012256.New,
			merkle.DefaultChunkLen, DefaultNumCPU)
	case Streebog512, gost.GOST3410512C:
		return gost34112012512.New()
	case Streebog512Merkle, gost.GOST3410512CMerkle:
		return gost.NewMerkleHasher(gost34112012512.New,
			merkle.DefaultChunkLen, DefaultNumCPU)
	case BLAKE2b, cmblake2b.Ed25519BLAKE2b, cmblake2b.Ed25519PhBLAKE2b:
		h, err := blake2b.New512(nil)
		if err != nil {
			panic(err)
		}
		return h
	case BLAKE2bMerkle, cmblake2b.Ed25519PhBLAKE2bMerkle:
		return cmblake2b.NewMerkleHasher(
			merkle.DefaultChunkLen, DefaultNumCPU)
	case BLAKE2b256:
		h, err := blake2b.New256(nil)
		if err != nil {
			panic(err)
		}
		return h
	case SHAKE128:
		return NewSHAKE128()
	case SHAKE256, SLHDSASHAKE256s, SLHDSASHAKE256sPh:
		return NewSHAKE256()
	case SHAKE128Merkle:
		return NewSHAKE128MerkleHasher(
			merkle.DefaultChunkLen, DefaultNumCPU)
	case SHAKE256Merkle, SLHDSASHAKE256sMerkle:
		return NewSHAKE256MerkleHasher(
			merkle.DefaultChunkLen, DefaultNumCPU)
	}
	return nil
}
