package sm9

import (
	"fmt"
	"io"
	"math/big"
	"strings"

	"xdx.jelly/xgcl/gerrors"
	"xdx.jelly/xgcl/gmath"
	"xdx.jelly/xgcl/grand"
	"xdx.jelly/xgcl/sm/sm3"
	"xdx.jelly/xgcl/sm/sm9/errors"
)

// 38635打印数据
const PrintIntermediaDataForSTD38635 = false

// Signature 签名数据结构
type Signature struct {
	H big.Int
	S G1
}

// NewSignature returns a new Signature.
//
// Deprecated, use `&Signature{}` or `new(Signature)`.
func NewSignature() *Signature {
	return &Signature{}
}

// MarshalBinary implements the encoding.BinaryMarshaler interface. It returns the byte slice of H || X || Y.
func (s *Signature) MarshalBinary() ([]byte, error) {
	data := make([]byte, 3*Sm9RefMaxLen)
	if err := gmath.FillBytes(&s.H, data[:Sm9RefMaxLen]); err != nil {
		return nil, err
	}
	s.S.FillBytes(data[Sm9RefMaxLen:])
	return data, nil
}

// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
// if return error, s will remain
func (s *Signature) UnmarshalBinary(data []byte) error {
	if len(data) != 3*Sm9RefMaxLen {
		return gerrors.WithAnnotating(errors.ErrInvalidInput, "input data too short")
	}
	s.H.SetBytes(data[:Sm9RefMaxLen])
	if s.H.Cmp(Order()) >= 0 || s.H.Sign() == 0 {
		return gerrors.WithAnnotating(errors.ErrInvalidInput, "Signature.H is zero or bigger than order N")
	}
	_, err := s.S.Unmarshal(data[Sm9RefMaxLen:])
	if err != nil {
		return gerrors.WithMessage(err, "Signature.S Unmarshal failed")
	}
	return nil
}

// String 实现Stringer接口
func (s *Signature) String() string {
	return fmt.Sprintf("SM9 Signature{\n\tH: %s,\n\tS: %s\n}", s.H.Text(16), s.S.String())
}

// Bytes output H || X || Y
func (s *Signature) Bytes() []byte {
	buf := make([]byte, 3*byteSize)
	copy(buf[:byteSize], gmath.BigIntToNByte(&s.H, byteSize))
	copy(buf[byteSize:], s.S.Marshal())
	return buf
}

func (s *Signature) Set(t *Signature) {
	s.H.Set(&t.H)
	s.S.Set(&t.S)
}

// SetBytes set signature from a byte slice
func (s *Signature) SetBytes(data []byte) error {
	if len(data) < 3*byteSize {
		return gerrors.WithAnnotating(errors.ErrInvalidInput, "input data too short")
	}
	data = data[:3*byteSize]
	s.H.SetBytes(data[:byteSize])
	s.S.Unmarshal(data[byteSize:])
	return nil
}

// Sign 签名
//
// rnd输入随机字节串或随机数发生器，可以为：
//   - []byte, 并且len(rnd)= 32
//   - io.Reader, 随机数发生器， 如crypto/rand.Reader, 也可以是包装的硬件随机数发生器
//   - nil, 则会使用默认的软件随机数发生器
func Sign(msg []byte, ds *UserSignKey, pubs *MastSignPublicKey, rnd interface{}) (*Signature, error) {
	if rnd == nil {
		rnd = grand.Reader
	}

	if b, ok := rnd.([]byte); ok {
		return sign(msg, ds, pubs, b)
	} else if reader, ok := rnd.(io.Reader); ok {
		b := make([]byte, numBytes)
		if _, err := reader.Read(b); err != nil {
			return nil, gerrors.ChainErrors(errors.ErrSignFailed, err)
		}
		return sign(msg, ds, pubs, b)
	} else {
		return nil, gerrors.WithAnnotating(errors.ErrSignFailed, "input rnd can only be nil, []byte or io.Reader")
	}
}

func sign(msg []byte, ds *UserSignKey, pubs *MastSignPublicKey, rnd []byte) (*Signature, error) {
	if len(rnd) != byteSize {
		return nil, gerrors.WithAnnotating(errors.ErrInvalidInput, "input rnd too short")
	}
	signature := &Signature{}
	r := new(big.Int)
	h := new(big.Int)
	g := &GT{}
	for {
		r.SetBytes(rnd[:byteSize])
		if pubs.e != nil {
			g.ScalarMult(pubs.e, r)
		} else {
			mulG1BaseThenPairing(g, &pubs.G2, r)
		}

		h = H2(msg, g.Marshal())
		r.Sub(r, h)
		r.Mod(r, Order())

		if PrintIntermediaDataForSTD38635 {
			fmt.Printf("计算群GT中的元素g = e (P1 , Ppub-s): %s\n", pubs.e.String())
			fmt.Printf("计算群GT中的元素w = g^r: %s\n", g.String())
			fmt.Printf("h: %s\n", strings.ToUpper(h.Text(16)))
			fmt.Printf("计算l = ( r - h ) mod N: %s\n", strings.ToUpper(r.Text(16)))
		}

		if r.Sign() != 0 {
			break
		}

		d := sm3.Sum(rnd)
		copy(rnd, d[:])
	}
	// Issues 5
	// s := ds.G1.ScalarMult(&ds.G1, r) //ds changed!
	s := (&G1{}).ScalarMult(&ds.G1, r)

	if PrintIntermediaDataForSTD38635 {
		fmt.Printf("计算群G1中的元素S: %s\n", s.String())
		fmt.Printf("消息M的签名为(h, S):\n")
		fmt.Printf("h: %s\n", strings.ToUpper(h.Text(16)))
		fmt.Printf("S: %s\n", s.String())
	}

	signature.H.Set(h)
	signature.S.Set(s)
	return signature, nil
}

// Verify 签名验证
func Verify(sig *Signature, uid, msg []byte, pubs *MastSignPublicKey) bool {
	h := new(big.Int).Set(&sig.H)
	if gmath.IsBigInt0(h) || h.Cmp(Order()) >= 0 {
		return false
	}

	if !sig.S.IsValid() || sig.S.IsZero() {
		return false
	}

	t := &GT{}
	mulG1BaseThenPairing(t, &pubs.G2, h)

	h1 := H1(uid, []byte{hidSign})
	p := new(G2).ScalarBaseMult(h1)
	p.Add(p, &pubs.G2)

	u := &GT{}
	pairing(u, &sig.S, p)

	if PrintIntermediaDataForSTD38635 {
		fmt.Printf("计算群GT中的元素g = e (P1 , Ppub-s): %s\n", pubs.e.String())
		fmt.Printf("计算群GT中的元素t = g^h: %s\n", t.String())
		fmt.Printf("计算h1 = H1(IDA||hid, N)\n")
		fmt.Printf("h1: %s\n", strings.ToUpper(h1.Text(16)))
		fmt.Printf("计算群G2中的元素P = [h1]P2 + Ppub-s = (xP , yP): %s\n", p.String())
		fmt.Printf("计算群GT中的元素u = e (S , P): %s\n", u.String())
	}

	u.Add(u, t) // In GT, Add is the same with Mul
	h2 := H2(msg, u.Marshal())
	if PrintIntermediaDataForSTD38635 {
		fmt.Printf("计算群GT中的元素w' = u*t: %s\n", u.String())
		fmt.Printf("计算h2 = H2(M||w, N): %s\n", strings.ToUpper(h.Text(16)))

	}
	return h2.Cmp(h) == 0
}
