using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshNet.Security.Cryptography.Ciphers;
namespace Renci.SshNet.Security.Cryptography
{
    /// 
    /// Base class for block cipher implementations.
    /// 
    public abstract class BlockCipher : SymmetricCipher
    {
        private CipherMode _mode;
        private CipherPadding _padding;
        /// 
        /// Gets the size of the block in bytes.
        /// 
        /// 
        /// The size of the block in bytes.
        /// 
        protected readonly int _blockSize;
        /// 
        /// Gets the size of the block.
        /// 
        /// 
        /// The size of the block.
        /// 
        public int BlockSize
        {
            get
            {
                return this._blockSize;
            }
        }
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The key.
        /// Size of the block.
        /// Cipher mode.
        /// Cipher padding.
        ///  is null.
        protected BlockCipher(byte[] key, int blockSize, CipherMode mode, CipherPadding padding)
            : base(key)
        {
            this._blockSize = blockSize;
            this._mode = mode;
            this._padding = padding;
            if (this._mode != null)
                this._mode.Init(this);
        }
        /// 
        /// Encrypts the specified data.
        /// 
        /// The data.
        /// Encrypted data
        public override byte[] Encrypt(byte[] data)
        {
            var output = new byte[data.Length];
            if (data.Length % this._blockSize > 0)
            {
                if (this._padding == null)
                {
                    throw new ArgumentException("data");
                }
                else
                {
                    data = this._padding.Pad(this._blockSize, data);
                }
            }
            var writtenBytes = 0;
            for (int i = 0; i < data.Length / this._blockSize; i++)
            {
                if (this._mode == null)
                {
                    writtenBytes += this.EncryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize);
                }
                else
                {
                    writtenBytes += this._mode.EncryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize);
                }
            }
            if (writtenBytes < data.Length)
            {
                throw new InvalidOperationException("Encryption error.");
            }
            return output;
        }
        /// 
        /// Decrypts the specified data.
        /// 
        /// The data.
        /// Decrypted data
        public override byte[] Decrypt(byte[] data)
        {
            if (data.Length % this._blockSize > 0)
            {
                {
                    if (this._padding == null)
                    {
                        throw new ArgumentException("data");
                    }
                    else
                    {
                        data = this._padding.Pad(this._blockSize, data);
                    }
                }
            }
            var output = new byte[data.Length];
            var writtenBytes = 0;
            for (int i = 0; i < data.Length / this._blockSize; i++)
            {
                if (this._mode == null)
                {
                    writtenBytes += this.DecryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize);
                }
                else
                {
                    writtenBytes += this._mode.DecryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize);
                }
            }
            if (writtenBytes < data.Length)
            {
                throw new InvalidOperationException("Encryption error.");
            }
            return output;
        }
    }
}