// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package btcutil

import (
	"crypto/ecdsa"
	"crypto/rand"
	"math/big"
)

// BlindRequesterState State
type BlindRequesterState struct {
	// secret stuff
	a, b, bInv, c *big.Int

	// shareable stuff
	F    *ecdsa.PublicKey
	X0   *big.Int //
	Mhat *big.Int // called m̂ in the paper
}

// BlindMessage Calculates a blinded version of message m
func BlindMessage(rState *BlindRequesterState, Q, R *ecdsa.PublicKey, m *big.Int) *big.Int {
	crv := Secp256k1().Params()

	// generate F which is not equal to O (§4.2)
	var err error
	F := new(ecdsa.PublicKey)
	for F.X == nil && F.Y == nil {
		// requester's three blinding factors (§4.2)
		rState.a, err = RandFieldElement(rand.Reader)
		maybePanic(err)
		rState.b, err = RandFieldElement(rand.Reader)
		maybePanic(err)
		rState.c, err = RandFieldElement(rand.Reader)
		maybePanic(err)
		rState.bInv = new(big.Int).ModInverse(rState.b, crv.N)

		// requester calculates point F (§4.2)
		abInv := new(big.Int).Mul(rState.a, rState.bInv)
		abInv.Mod(abInv, crv.N)
		bInvR := ScalarMult(rState.bInv, R)
		abInvQ := ScalarMult(abInv, Q)
		cG := ScalarBaseMult(rState.c)
		F = Add(bInvR, abInvQ)
		F = Add(F, cG)
	}
	rState.F = F

	// calculate r and m̂
	r := new(big.Int).Mod(F.X, crv.N)
	mHat := new(big.Int).Mul(rState.b, r)
	mHat.Mul(mHat, m)
	mHat.Add(mHat, rState.a)
	mHat.Mod(mHat, crv.N)
	rState.Mhat = mHat

	return rState.Mhat
}

// BlindExtract Extract true signature from the blind signature
func BlindExtract(rState *BlindRequesterState, sHat *big.Int) *BlindSignature {
	crv := Secp256k1().Params()

	// requester extracts the real signature (§4.4)
	s := new(big.Int).Mul(rState.bInv, sHat)
	s.Add(s, rState.c)
	s.Mod(s, crv.N)
	sig := &BlindSignature{S: s, F: rState.F}
	return sig
}

func maybePanic(err error) {
	if err != nil {
		panic(err)
	}
}
