///
/// Copyright (c) 2018 xdx. All rights reserved.
///
/// \file: ecb.go
///
/// \brief: GO not implements the ECB modes for sefety.
/// See: https://code.google.com/p/go/issues/detail?id=5597
/// rsc commented on 31 May 2013
/// Comment 1:
/// Why? We left ECB out intentionally: it's insecure, and if needed it's
/// trivial to implement.
///
/// \author: xdx
///

package modes

import "crypto/cipher"

type Encrypt4 interface {
	Encrypt4(dst []byte, src []byte)
}

type Decrypt4 interface {
	Decrypt4(dst []byte, src []byte)
}

type ecb struct {
	b         cipher.Block
	blockSize int
}

type ecbEncrypter ecb
type ecbDecrypter ecb

func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
func (x *ecbDecrypter) BlockSize() int { return x.blockSize }

// NewECBEncrypter is the BlockMode of ECB of encrypt
func NewECBEncrypter(b cipher.Block) cipher.BlockMode {
	return &ecbEncrypter{b, b.BlockSize()}
}

// NewECBDecrypter is the BlockMode of ECB of decrypt
func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
	return &ecbDecrypter{b, b.BlockSize()}
}

func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
	if len(src)%x.blockSize != 0 {
		panic("xdx.jelly/xgcl/sm/sm4: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("xdx.jelly/xgcl/sm/sm4: output smaller than input")
	}
	if b4, ok := x.b.(Encrypt4); ok {
		for len(src) >= 4*x.blockSize {
			b4.Encrypt4(dst[:x.blockSize], src[:x.blockSize])
			src = src[4*x.blockSize:]
			dst = dst[4*x.blockSize:]
		}
	}
	for len(src) > 0 {
		x.b.Encrypt(dst[:x.blockSize], src[:x.blockSize])
		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}

func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
	if len(src)%x.blockSize != 0 {
		panic("xdx.jelly/xgcl/sm/sm4: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("xdx.jelly/xgcl/sm/sm4: output smaller than input")
	}
	if b4, ok := x.b.(Decrypt4); ok {
		for len(src) >= 4*x.blockSize {
			b4.Decrypt4(dst[:x.blockSize], src[:x.blockSize])
			src = src[4*x.blockSize:]
			dst = dst[4*x.blockSize:]
		}
	}
	for len(src) > 0 {
		x.b.Decrypt(dst[:x.blockSize], src[:x.blockSize])
		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}

}
