package sm9m

import (
	"encoding"

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

type ClientDecContext struct {
	cipher *sm9.Cipher
}

type ClientDecParam struct {
	c1 sm9.G1 // cipher.C1
}

type ServerDecParam struct {
	e sm9.GT // e(C, Ks)
}

func (ctx *ClientDecContext) GenerateParam(c *sm9.Cipher) (*ClientDecParam, error) {
	if !c.C1.IsValid() {
		return nil, ErrInvalidCipherC1
	}
	ctx.cipher = c
	param := &ClientDecParam{}
	param.c1.Set(&c.C1)
	return param, nil
}

func (ctx *ClientDecContext) DecryptFinal(id []byte, clientEncKey *EncKeyClient, param *ServerDecParam) ([]byte, error) {
	w := sm9.Pairing(&ctx.cipher.C1, &clientEncKey.p)
	w.Mul(w, &param.e) // e = e(C1, de)

	c := ctx.cipher
	keylen, f, err := sm9.DecryptParams(c)
	if err != nil {
		return nil, err
	}
	key := make([]byte, keylen)
	_ = sm9.Kdf(key, c.C1.Marshal(), w.Marshal(), id)
	return sm9.DecodeCipher(c, key, f)
}

func ServerDecrypt(clientParam *ClientDecParam, serverEncKey *EncKeyServer) (*ServerDecParam, error) {
	if !clientParam.c1.IsValid() {
		return nil, ErrInvalidCipherC1
	}

	w := sm9.Pairing(&clientParam.c1, &serverEncKey.p)
	return &ServerDecParam{e: *w}, nil
}

var _ encoding.BinaryMarshaler = &ClientDecParam{}
var _ encoding.BinaryUnmarshaler = &ClientDecParam{}

var _ encoding.BinaryMarshaler = &ServerDecParam{}
var _ encoding.BinaryUnmarshaler = &ServerDecParam{}

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

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

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

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