// 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 secp256k1

import (
	"bytes"
	"errors"
	"fmt"

	"github.com/33cn/chain33/common/crypto"
	secp256k1 "github.com/btcsuite/btcd/btcec"
)

//Driver 驱动
type Driver struct{}

//GenKey 生成私钥
func (d Driver) GenKey() (crypto.PrivKey, error) {
	privKeyBytes := [32]byte{}
	copy(privKeyBytes[:], crypto.CRandBytes(32))
	priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKeyBytes[:])
	copy(privKeyBytes[:], priv.Serialize())
	return PrivKeySecp256k1(privKeyBytes), nil
}

//PrivKeyFromBytes 字节转为私钥
func (d Driver) PrivKeyFromBytes(b []byte) (privKey crypto.PrivKey, err error) {
	if len(b) != 32 {
		return nil, errors.New("invalid priv key byte")
	}
	privKeyBytes := new([32]byte)
	copy(privKeyBytes[:], b[:32])
	priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKeyBytes[:])
	copy(privKeyBytes[:], priv.Serialize())
	return PrivKeySecp256k1(*privKeyBytes), nil
}

//PubKeyFromBytes 字节转为公钥
func (d Driver) PubKeyFromBytes(b []byte) (pubKey crypto.PubKey, err error) {
	if len(b) != 33 {
		return nil, errors.New("invalid pub key byte")
	}
	pubKeyBytes := new([33]byte)
	copy(pubKeyBytes[:], b[:])
	return PubKeySecp256k1(*pubKeyBytes), nil
}

//SignatureFromBytes 字节转为签名
func (d Driver) SignatureFromBytes(b []byte) (sig crypto.Signature, err error) {
	return SignatureSecp256k1(b), nil
}

//PrivKeySecp256k1 PrivKey
type PrivKeySecp256k1 [32]byte

//Bytes 字节格式
func (privKey PrivKeySecp256k1) Bytes() []byte {
	s := make([]byte, 32)
	copy(s, privKey[:])
	return s
}

//Sign 签名
func (privKey PrivKeySecp256k1) Sign(msg []byte) crypto.Signature {
	priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:])
	sig, err := priv.Sign(crypto.Sha256(msg))
	if err != nil {
		panic("Error signing secp256k1" + err.Error())
	}
	return SignatureSecp256k1(sig.Serialize())
}

//PubKey 私钥生成公钥
func (privKey PrivKeySecp256k1) PubKey() crypto.PubKey {
	_, pub := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:])
	var pubSecp256k1 PubKeySecp256k1
	copy(pubSecp256k1[:], pub.SerializeCompressed())
	return pubSecp256k1
}

//Equals 私钥是否相等
func (privKey PrivKeySecp256k1) Equals(other crypto.PrivKey) bool {
	if otherSecp, ok := other.(PrivKeySecp256k1); ok {
		return bytes.Equal(privKey[:], otherSecp[:])
	}
	return false

}

func (privKey PrivKeySecp256k1) String() string {
	return fmt.Sprintf("PrivKeySecp256k1{*****}")
}

// PubKey

//PubKeySecp256k1 Compressed pubkey (just the x-cord),
// prefixed with 0x02 or 0x03, depending on the y-cord.
type PubKeySecp256k1 [33]byte

//Bytes 字节格式
func (pubKey PubKeySecp256k1) Bytes() []byte {
	s := make([]byte, 33)
	copy(s, pubKey[:])
	return s
}

//VerifyBytes 验证字节
func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig crypto.Signature) bool {
	// unwrap if needed
	if wrap, ok := sig.(SignatureS); ok {
		sig = wrap.Signature
	}
	// and assert same algorithm to sign and verify
	sigSecp256k1, ok := sig.(SignatureSecp256k1)
	if !ok {
		return false
	}

	pub, err := secp256k1.ParsePubKey(pubKey[:], secp256k1.S256())
	if err != nil {
		return false
	}
	sig2, err := secp256k1.ParseDERSignature(sigSecp256k1[:], secp256k1.S256())
	if err != nil {
		return false
	}
	return sig2.Verify(crypto.Sha256(msg), pub)
}

func (pubKey PubKeySecp256k1) String() string {
	return fmt.Sprintf("PubKeySecp256k1{%X}", pubKey[:])
}

//KeyString Must return the full bytes in hex.
// Used for map keying, etc.
func (pubKey PubKeySecp256k1) KeyString() string {
	return fmt.Sprintf("%X", pubKey[:])
}

//Equals 公钥相等
func (pubKey PubKeySecp256k1) Equals(other crypto.PubKey) bool {
	if otherSecp, ok := other.(PubKeySecp256k1); ok {
		return bytes.Equal(pubKey[:], otherSecp[:])
	}
	return false

}

//SignatureSecp256k1 Signature
type SignatureSecp256k1 []byte

//SignatureS 签名
type SignatureS struct {
	crypto.Signature
}

//Bytes 字节格式
func (sig SignatureSecp256k1) Bytes() []byte {
	s := make([]byte, len(sig))
	copy(s, sig[:])
	return s
}

//IsZero 是否是0
func (sig SignatureSecp256k1) IsZero() bool { return len(sig) == 0 }

func (sig SignatureSecp256k1) String() string {
	fingerprint := make([]byte, len(sig[:]))
	copy(fingerprint, sig[:])
	return fmt.Sprintf("/%X.../", fingerprint)

}

//Equals 相等
func (sig SignatureSecp256k1) Equals(other crypto.Signature) bool {
	if otherEd, ok := other.(SignatureSecp256k1); ok {
		return bytes.Equal(sig[:], otherEd[:])
	}
	return false

}

//const
const (
	Name = "secp256k1"
	ID   = 1
)

func init() {
	crypto.Register(Name, &Driver{})
	crypto.RegisterType(Name, ID)
}
