package sm9m

import (
	"crypto/rand"
	"encoding"
	"io"
	"math/big"

	"xdx.jelly/xgcl/gerrors"
	"xdx.jelly/xgcl/gmath"
	"xdx.jelly/xgcl/sm/sm9"
)

// 一、签名密钥分割方式，ds= [Ks] * Kc,
// 客户端掌握Kc, 服务端掌握 Ks \in G1

// SignKeyClient 客户端部分签名密钥
type SignKeyClient struct {
	p sm9.G1
}

// SignKeyServer 服务端部分签名密钥
type SignKeyServer struct {
	k big.Int
}

// GenerateKeys 从完整的SM9用户密钥ds生成客户端和服务端部分密钥。
// ds为KGC下发的用户密钥，应在调用完成后清除。
func GenerateSignKeys(ds *sm9.UserSignKey, rnd io.Reader) (*SignKeyClient, *SignKeyServer, error) {
	i, j, err := generatePairInt(rnd, sm9.N)
	if err != nil {
		return nil, nil, gerrors.WithAnnotating(err, "GenerateSignKeys failed")
	}
	kc := &SignKeyClient{}
	ks := &SignKeyServer{}
	ks.k.Set(i)
	kc.p.ScalarMult(&ds.G1, j)
	return kc, ks, nil
}

// 二、加密密钥分割方式，de= Kc + Ks,
// 客户端掌握Kc, 服务端掌握 Ks

// EncKeyClient 客户端部分签名密钥
type EncKeyClient struct {
	p sm9.G2
}

// EncKeyServer 服务端部分签名密钥
type EncKeyServer struct {
	p sm9.G2
}

// GenerateEncKeys 从完整的SM9用户密钥de生成客户端和服务端部分密钥。
// de为KGC下发的用户密钥，应在调用完成后清除。
func GenerateEncKeys(de *sm9.UserEncKey, rnd io.Reader) (*EncKeyClient, *EncKeyServer, error) {
	var k *big.Int
	var err error
	for {
		k, err = rand.Int(rnd, sm9.N)
		if err != nil {
			return nil, nil, err
		}
		if k.Sign() > 0 {
			break
		}
	}

	kc := &EncKeyClient{}
	ks := &EncKeyServer{}
	kc.p.ScalarBaseMult(k)
	ks.p.Neg(&kc.p)
	ks.p.Add(&de.G2, &ks.p)
	return kc, ks, nil
}

// generatePairInt 生成k,s满足k * s = 1 mod n, 0 < k,s < n.
func generatePairInt(rnd io.Reader, n *big.Int) (k *big.Int, s *big.Int, err error) {
	for {
		k, err = rand.Int(rnd, n)
		if err != nil {
			return nil, nil, err
		}
		if k.Sign() > 0 {
			break
		}
	}
	s = new(big.Int).ModInverse(k, n)
	return k, s, nil
}

var _ encoding.BinaryMarshaler = &EncKeyClient{}
var _ encoding.BinaryUnmarshaler = &EncKeyClient{}
var _ encoding.BinaryMarshaler = &EncKeyServer{}
var _ encoding.BinaryUnmarshaler = &EncKeyServer{}

var _ encoding.BinaryMarshaler = &SignKeyClient{}
var _ encoding.BinaryUnmarshaler = &SignKeyClient{}
var _ encoding.BinaryMarshaler = &SignKeyServer{}
var _ encoding.BinaryUnmarshaler = &SignKeyServer{}

// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (k *EncKeyServer) UnmarshalBinary(data []byte) error {
	_, err := k.p.Unmarshal(data)
	return err
}

// MarshalBinary implements encoding.BinaryMarshaler
func (k *EncKeyServer) MarshalBinary() (data []byte, err error) {
	return k.p.Marshal(), nil
}

// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (k *EncKeyClient) UnmarshalBinary(data []byte) error {
	_, err := k.p.Unmarshal(data)
	return err
}

// MarshalBinary implements encoding.BinaryMarshaler
func (k *EncKeyClient) MarshalBinary() (data []byte, err error) {
	return k.p.Marshal(), nil
}

// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (k *SignKeyClient) UnmarshalBinary(data []byte) error {
	_, err := k.p.Unmarshal(data)
	return err
}

// MarshalBinary implements encoding.BinaryMarshaler
func (k *SignKeyClient) MarshalBinary() (data []byte, err error) {
	return k.p.Marshal(), nil
}

// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (k *SignKeyServer) UnmarshalBinary(data []byte) error {
	if len(data) > sm9.ByteSize() {
		return ErrInvalidInput
	}
	k.k.SetBytes(data)
	if k.k.Cmp(sm9.N) >= 0 {
		return ErrKeyTooBig
	}
	if k.k.Sign() == 0 {
		return ErrKeyIsZero
	}
	return nil
}

// MarshalBinary implements encoding.BinaryMarshaler
func (k *SignKeyServer) MarshalBinary() (data []byte, err error) {
	data = make([]byte, sm9.ByteSize())
	if err := gmath.FillBytes(&k.k, data); err != nil {
		return nil, err
	}
	return data, nil
}
