package dkgc

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

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

type ServerKey struct {
	big.Int
}

type ClientKey = sm9.UserSignKey

func ServerGenerateKey(r io.Reader) (sk *ServerKey, basePoint *ClientKey, err error) {
	for {
		x, err := rand.Int(r, sm9.Order())
		if err != nil {
			return nil, nil, err
		}
		if x.Sign() == 0 {
			continue
		}
		sk = &ServerKey{
			Int: *x,
		}
		basePoint = &ClientKey{}
		y := new(big.Int).Set(x)
		y.ModInverse(y, sm9.Order())
		basePoint.G1.ScalarBaseMult(y)
		return sk, basePoint, nil
	}
}

type ClientSignContext struct {
	r1 big.Int
}

func (csc *ClientSignContext) ComputeSignData(r io.Reader, pubs *sm9.MastSignPublicKey) (u *sm9.GT, err error) {
	for {
		x, err := rand.Int(r, sm9.Order())
		if err != nil {
			return nil, err
		}
		if x.Sign() == 0 {
			continue
		}
		csc.r1 = *x
		break
	}
	u = sm9.Pairing(sm9.G1Generator(), &pubs.G2)
	u.ScalarMult(u, &csc.r1)
	return u, nil
}

func (csc *ClientSignContext) ComputeSignature(id []byte, h, k1, k2 *big.Int, kc *ClientKey) (*sm9.Signature, error) {
	sig := &sm9.Signature{}
	sig.H = *h
	x := new(big.Int)
	x.Mul(&csc.r1, k1)
	x.Add(x, k2)
	sig.S.ScalarMult(&kc.G1, x)
	return sig, nil
}

func ServerComputeSignData(r io.Reader, u *sm9.GT, m []byte, pubs *sm9.MastSignPublicKey, ks *ServerKey) (h, k1, k2 *big.Int, err error) {
	r2 := new(big.Int)
	r3 := new(big.Int)
	for {
		r2, err = rand.Int(r, sm9.Order())
		if err != nil {
			return nil, nil, nil, err
		}
		if r2.Sign() == 0 {
			continue
		}
		break
	}

	for {
		r3, err = rand.Int(r, sm9.Order())
		if err != nil {
			return nil, nil, nil, err
		}
		if r3.Sign() == 0 {
			continue
		}
		break
	}

	g := sm9.Pairing(sm9.G1Generator(), &pubs.G2)
	g.ScalarMult(g, r3)
	w := new(sm9.GT)
	w.ScalarMult(u, r2)
	w.Add(w, g)
	h = sm9.H2(m, w.Marshal())

	k1 = new(big.Int).Mul(r2, &ks.Int)
	k1.Mod(k1, sm9.Order())
	k2 = new(big.Int).Sub(r3, h)
	k2.Mul(k2, &ks.Int)
	k2.Mod(k2, sm9.Order())
	return h, k1, k2, nil
}
