//package main

package sm_crypto
import (
	"crypto/elliptic"
	"encoding/hex"
	"errors"
	"fmt"
	"github.com/TMChain/go-TMChain/common"
	"github.com/TMChain/go-TMChain/common/math"
	"github.com/TMChain/go-TMChain/crypto/crypto_interface"
	"github.com/TMChain/go-TMChain/crypto/sm_crypto/sm2"
	"github.com/TMChain/go-TMChain/crypto/sm_crypto/sm3"
	"github.com/TMChain/go-TMChain/rlp"
	"io"
	"io/ioutil"
	"math/big"
	"os"

	"C"
	"github.com/TMChain/go-TMChain/common/hexutil"
)

func main() {

}

var errInvalidSm2Pubkey = errors.New("invalid sm2 public key")

//export C_Hash256
func C_Hash256(dataC *C.char) *C.char {
	data := C.GoString(dataC)
	s := sm3.New()
	s.Write(common.FromHex(data))

	return C.CString(hexutil.Encode(s.Sum(nil)))
}

//export C_Hash256Bysha3
func C_Hash256Bysha3(dataC *C.char) *C.char {
	//data := C.GoString(dataC)
	////s := sha3.New256()
	//s := sha3.NewLegacyKeccak256()
	//s.Write(common.FromHex(data))
	//
	//return C.CString(hexutil.Encode(s.Sum(nil)))
	return nil
}

//export C_Sign
func C_Sign(hashC *C.char, prvC *C.char) (*C.char) {
	hash := C.GoString(hashC)
	prv := C.GoString(prvC)

	priv, err := toPriKey_c(common.FromHex(prv))
	if err != nil {
		return C.CString("")
	}
	if len(common.FromHex(hash)) != 32 {
		return C.CString("")
	}

	sigRet, err := sm2.SM2Sign(&sm2.PrivateKey{
		PublicKey: sm2.PublicKey{Curve: priv.Curve, X: priv.X, Y: priv.Y},
		D:         priv.D,
	}, common.FromHex(hash))

	if err != nil {
		return C.CString("")
	}

	return C.CString(hexutil.Encode(sigRet))
}

//export C_GenerateKey
func C_GenerateKey() (*C.char) {
	eprk, err := sm2.GenerateKey()
	if err != nil {
		return C.CString("")
	}
	priv := &icrypto.PrivateKey{
		PublicKey: icrypto.PublicKey{
			Curve: eprk.Curve,
			X:     eprk.X,
			Y:     eprk.Y,
		},
		D: eprk.D,
	}
	return C.CString(fromPriKey_c(priv))
}

//export C_FromPrv
func C_FromPrv(prvC *C.char)(*C.char) {
	prv := C.GoString(prvC)
	priv, err := toPriKey_c(common.FromHex(prv))
	if err != nil {
		return C.CString("")
	}
	smC := new(SmCrypto)
	pubByte := smC.FromPub(&priv.PublicKey)

	return C.CString(hexutil.Encode(pubByte))
}

//export C_VerifySignature
func C_VerifySignature(pubkeyC, hashC, signatureC *C.char) bool {
	hash := C.GoString(hashC)
	pubkey := C.GoString(pubkeyC)
	signature := C.GoString(signatureC)
	pub, err := unmarshalPubkey_c(common.FromHex(pubkey))
	if err != nil {
		return false
	}

	if sm2.VerifySign(&sm2.PublicKey{Curve: pub.Curve, X: pub.X, Y: pub.Y}, common.FromHex(hash), common.FromHex(signature)) {
		return true
	}
	return false
}

// FromECDSA exports a private key into a binary dump.
func fromSm2_c(priv *icrypto.PrivateKey) []byte {
	if priv == nil {
		return nil
	}

	return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
}

func fromPriKey_c(priv *icrypto.PrivateKey) string {
	return common.ToHex(fromSm2_c(priv))
}

func toPriKey_c(d []byte) (*icrypto.PrivateKey, error) {
	return toSm2_c(d)
}

func toSm2_c(d []byte) (*icrypto.PrivateKey, error) {
	return toSm2(d, true)
}

func unmarshalPubkey_c(pub []byte) (*icrypto.PublicKey, error) {
	x, y := elliptic.Unmarshal(sm2.P256Sm2(), pub)
	if x == nil {
		return nil, errInvalidSm2Pubkey
	}
	return &icrypto.PublicKey{Curve: sm2.P256Sm2(), X: x, Y: y}, nil
}




//sm2加密抽象
type SmCrypto struct {
}

// Keccak256 calculates and returns the Keccak256 hash of the input data.
func (e *SmCrypto) X256(data ...[]byte) []byte {
	s := sm3.New()
	for _, b := range data {
		s.Write(b)
	}

	return s.Sum(nil)
}

// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
// converting it to an internal Hash data structure.
func (e *SmCrypto) X256Hash(data ...[]byte) (h common.Hash) {
	s := sm3.New()
	for _, b := range data {
		s.Write(b)
	}
	s.Sum(h[:0])

	return h
}

// Keccak512 calculates and returns the Keccak512 hash of the input data.
func (e *SmCrypto) X512(data ...[]byte) []byte {
	//d := sha3.NewLegacyKeccak512()
	//for _, b := range data {
	//	d.Write(b)
	//}
	//return d.Sum(nil)
	return nil
}

// CreateAddress creates an ethereum address given the bytes and the nonce
func (e *SmCrypto) CreateAddress(b common.Address, nonce uint64) common.Address {
	data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
	return common.BytesToAddress(e.X256(data)[12:])
}

// CreateAddress2 creates an ethereum address given the address bytes, initial
// contract code hash and a salt.
func (e *SmCrypto) CreateAddress2(b common.Address, salt [32]byte, inithash []byte) common.Address {
	return common.BytesToAddress(e.X256([]byte{0xff}, b.Bytes(), salt[:], inithash)[12:])
}

func (e *SmCrypto) ToPriKey(d []byte) (*icrypto.PrivateKey, error) {
	return e.ToSm2(d)
}

// ToECDSA creates a private key with the given D value.
func (e *SmCrypto) ToSm2(d []byte) (*icrypto.PrivateKey, error) {
	return toSm2(d, true)
}

func (e *SmCrypto) ToPriKeyUnsafe(d []byte) *icrypto.PrivateKey {
	return e.ToSm2Unsafe(d)
}

// ToECDSAUnsafe blindly converts a binary blob to a private key. It should almost
// never be used unless you are sure the input is valid and want to avoid hitting
// errors due to bad origin encoding (0 prefixes cut off).
func (e *SmCrypto) ToSm2Unsafe(d []byte) *icrypto.PrivateKey {
	priv, _ := toSm2(d, false)
	return priv
}

// toECDSA creates a private key with the given D value. The strict parameter
// controls whether the key's length should be enforced at the curve size or
// it can also accept legacy encodings (0 prefixes).
func toSm2(d []byte, strict bool) (*icrypto.PrivateKey, error) {
	priv := new(sm2.PrivateKey)
	priv.PublicKey.Curve = sm2.P256Sm2()
	if strict && 8*len(d) != priv.Params().BitSize {
		return nil, fmt.Errorf("invalid length, need %d bits", priv.Params().BitSize)
	}
	priv.D = new(big.Int).SetBytes(d)

	// The priv.D must < N
	//if priv.D.Cmp(secp256k1N) >= 0 {
	//	return nil, fmt.Errorf("invalid private key, >=N")
	//}
	// The priv.D must not be zero or negative.
	if priv.D.Sign() <= 0 {
		return nil, fmt.Errorf("invalid private key, zero or negative")
	}

	priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(d)
	if priv.PublicKey.X == nil {
		return nil, errors.New("invalid private key")
	}
	//需要赋值转换为接口定义的私钥形式
	return &icrypto.PrivateKey{
		D: priv.D,
		PublicKey: icrypto.PublicKey{
			Curve: priv.Curve,
			X:     priv.X,
			Y:     priv.Y,
		},
	}, nil
}

func (e *SmCrypto) FromPriKey(priv *icrypto.PrivateKey) []byte {
	return e.FromSm2(priv)
}

// FromECDSA exports a private key into a binary dump.
func (e *SmCrypto) FromSm2(priv *icrypto.PrivateKey) []byte {
	if priv == nil {
		return nil
	}

	return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
}

// UnmarshalPubkey converts bytes to a secp256k1 public key.
func (e *SmCrypto) UnmarshalPubkey(pub []byte) (*icrypto.PublicKey, error) {
	x, y := elliptic.Unmarshal(sm2.P256Sm2(), pub)
	if x == nil {
		return nil, errInvalidSm2Pubkey
	}
	return &icrypto.PublicKey{Curve: sm2.P256Sm2(), X: x, Y: y}, nil
}

func (e *SmCrypto) FromPub(pub *icrypto.PublicKey) []byte {
	return e.FromSM2Pub(pub)
}

func (e *SmCrypto) FromSM2Pub(pub *icrypto.PublicKey) []byte {
	if pub == nil || pub.X == nil || pub.Y == nil {
		return nil
	}
	return elliptic.Marshal(sm2.P256Sm2(), pub.X, pub.Y)
}

func (e *SmCrypto) HexToPriKey(hexkey string) (*icrypto.PrivateKey, error) {
	return e.HexToSm2(hexkey)
}

// HexToECDSA parses a secp256k1 private key.
func (e *SmCrypto) HexToSm2(hexkey string) (*icrypto.PrivateKey, error) {
	b, err := hex.DecodeString(hexkey)
	if err != nil {
		return nil, errors.New("invalid hex string")
	}
	return e.ToSm2(b)
}

func (e *SmCrypto) LoadPriKey(file string) (*icrypto.PrivateKey, error) {
	return e.LoadSm2(file)
}

// LoadECDSA loads a secp256k1 private key from the given file.
func (e *SmCrypto) LoadSm2(file string) (*icrypto.PrivateKey, error) {
	buf := make([]byte, 64)
	fd, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	defer fd.Close()
	if _, err := io.ReadFull(fd, buf); err != nil {
		return nil, err
	}

	key, err := hex.DecodeString(string(buf))
	if err != nil {
		return nil, err
	}
	return e.ToSm2(key)
}

func (e *SmCrypto) SavePriKey(file string, key *icrypto.PrivateKey) error {
	return e.SaveSm2(file, key)
}

// SaveECDSA saves a secp256k1 private key to the given file with
// restrictive permissions. The key data is saved hex-encoded.
func (e *SmCrypto) SaveSm2(file string, key *icrypto.PrivateKey) error {
	k := hex.EncodeToString(e.FromSm2(key))
	return ioutil.WriteFile(file, []byte(k), 0600)
}

func (e *SmCrypto) GenerateKey() (*icrypto.PrivateKey, error) {
	eprk, err := sm2.GenerateKey()
	return &icrypto.PrivateKey{
		PublicKey: icrypto.PublicKey{
			Curve: eprk.Curve,
			X:     eprk.X,
			Y:     eprk.Y,
		},
		D: eprk.D,
	}, err
}

func (e *SmCrypto) GenerateKey2(rand io.Reader) (*icrypto.PrivateKey, error) {
	eprk, err := sm2.GenerateKey2(rand)
	return &icrypto.PrivateKey{
		PublicKey: icrypto.PublicKey{
			Curve: eprk.Curve,
			X:     eprk.X,
			Y:     eprk.Y,
		},
		D: eprk.D,
	}, err
}

// ValidateSignatureValues verifies whether the signature values are valid with
// the given chain rules. The v value is assumed to be either 0 or 1.
func (e *SmCrypto) ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool {
	//if r.Cmp(common.Big1) < 0 || s.Cmp(common.Big1) < 0 {
	//	return false
	//}
	// reject upper range of s values (ECDSA malleability)
	// see discussion in secp256k1/libsecp256k1/include/secp256k1.h
	//if homestead && s.Cmp(secp256k1halfN) > 0 {
	//	return false
	//}
	// Frontier: allow s to be in full N range
	//return r.Cmp(secp256k1N) < 0 && s.Cmp(secp256k1N) < 0 && (v == 0 || v == 1)
	return true
}

func (e *SmCrypto) PubkeyToAddress(p *icrypto.PublicKey) common.Address {
	pubBytes := e.FromSM2Pub(p)
	return common.BytesToAddress(e.X256(pubBytes[1:])[12:])
}

func zeroBytes(bytes []byte) {
	for i := range bytes {
		bytes[i] = 0
	}
}

// Ecrecover returns the uncompressed public key that created the given signature.
func (e *SmCrypto) Ecrecover(pub, hash, sig []byte) ([]byte, error) {
	pk, err := e.UnmarshalPubkey(pub)
	if err != nil {
		return nil, errors.New("sm2 Ecrecover-UnmarshalPubkey err")
	}
	if sm2.VerifySign(&sm2.PublicKey{Curve: pk.Curve, X: pk.X, Y: pk.Y}, hash, sig) {
		return pub, nil
	}

	return nil, errors.New("sm2 Ecrecover-VerifySign err")
}

// SigToPub returns the public key that created the given signature.
func (e *SmCrypto) SigToPub(pub, hash, sig []byte) (*icrypto.PublicKey, error) {
	s, err := e.Ecrecover(pub, hash, sig)
	if err != nil {
		return nil, err
	}

	x, y := elliptic.Unmarshal(sm2.P256Sm2(), s)

	return &icrypto.PublicKey{Curve: sm2.P256Sm2(), X: x, Y: y}, nil
}

// Sign calculates an ECDSA signature.
//
// This function is susceptible to chosen plaintext attacks that can leak
// information about the private key that is used for signing. Callers must
// be aware that the given hash cannot be chosen by an adversery. Common
// solution is to hash any input before calculating the signature.
//
// The produced signature is in the [R || S || V] format where V is 0 or 1.
func (e *SmCrypto) Sign(hash []byte, prv *icrypto.PrivateKey) (sig []byte, err error) {
	if len(hash) != 32 {
		return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
	}

	return sm2.SM2Sign(&sm2.PrivateKey{
		PublicKey: sm2.PublicKey{Curve: prv.Curve, X: prv.X, Y: prv.Y},
		D:         prv.D,
	}, hash)
}

// VerifySignature checks that the given public key created signature over hash.
// The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
// The signature should have the 64 byte [R || S] format.
func (e *SmCrypto) VerifySignature(pubkey, hash, signature []byte) bool {
	pub, err := e.UnmarshalPubkey(pubkey)
	if err != nil {
		return false
	}

	if sm2.VerifySign(&sm2.PublicKey{Curve: pub.Curve, X: pub.X, Y: pub.Y}, hash, signature) {
		return true
	}
	return false
}

// DecompressPubkey parses a public key in the 33-byte compressed format.
func (e *SmCrypto) DecompressPubkey(pubkey []byte) (*icrypto.PublicKey, error) {
	pk := sm2.Decompress(pubkey)
	return &icrypto.PublicKey{Curve: pk.Curve, X: pk.X, Y: pk.Y}, nil
}

// CompressPubkey encodes a public key to the 33-byte compressed format.
func (e *SmCrypto) CompressPubkey(pubkey *icrypto.PublicKey) []byte {
	return sm2.Compress(&sm2.PublicKey{Curve: pubkey.Curve, X: pubkey.X, Y: pubkey.Y})
}

// S256 returns an instance of the secp256k1 curve.
func (e *SmCrypto) S256() elliptic.Curve {
	return sm2.P256Sm2()
}
