package sm1

import (
	"crypto/cipher"

	"xdx.jelly/xgcl/gerrors"
)

// BlockSize is the SM1 block size in bytes.
const (
	BlockSize  = 16
	numOfRound = 8
)

// sm1Cipher is an instance of cipher.Block interface
type sm1Cipher struct {
	encKey [4 * (numOfRound + 1)]uint32
	decKey [4 * (numOfRound + 1)]uint32
}

// NewCipher creates and returns a new cipher.Block.
func NewCipher(key []byte) (cipher.Block, error) {
	if len(key) != BlockSize {
		return nil, gerrors.WithAnnotatingf(ErrInvalidInputLength, "key should be %d bytes", BlockSize)
	}

	cp := new(sm1Cipher)
	// gen keys
	keyexpand_enc(cp.encKey[:], key)
	keyexpand_dec(cp.decKey[:], key)

	return cp, nil
}

// Clear implements the gmath.Clearable interface
func (c *sm1Cipher) Clear() {
	for i := 0; i < len(c.decKey); i++ {
		c.decKey[i] = 0
		c.encKey[i] = 0
	}
}

func (c *sm1Cipher) BlockSize() int { return BlockSize }

// 调用前应保证输入一个block
func (c *sm1Cipher) Encrypt(dst, src []byte) {
	if len(src) < BlockSize {
		panic("xdx.jelly/xgcl/sm/sm1/Encrypt: input not full block")
	}
	if len(dst) < BlockSize {
		panic("xdx.jelly/xgcl/sm/sm1/Encrypt: output not full block")
	}
	encryptBlock(dst, src, c.encKey[:])
}

func (c *sm1Cipher) Decrypt(dst, src []byte) {
	if len(src) < BlockSize {
		panic("xdx.jelly/xgcl/sm/sm1/Decrypt: input not full block")
	}
	if len(dst) < BlockSize {
		panic("xdx.jelly/xgcl/sm/sm1/Decrypt: output not full block")
	}
	decryptBlock(dst, src, c.decKey[:])
}
