#if NET
using System;
using System.Security.Cryptography;

namespace Renci.SshNet.Security
{
    internal abstract partial class KeyExchangeECDH
    {
        private sealed class BclImpl : Impl
        {
            private readonly ECCurve _curve;
            private readonly ECDiffieHellman _clientECDH;

            public BclImpl(ECCurve curve)
            {
                _curve = curve;
                _clientECDH = ECDiffieHellman.Create();
            }

            public override byte[] GenerateClientECPoint()
            {
                _clientECDH.GenerateKey(_curve);

                var q = _clientECDH.PublicKey.ExportParameters().Q;

                return EncodeECPoint(q);
            }

            public override byte[] CalculateAgreement(byte[] serverECPoint)
            {
                var q = DecodeECPoint(serverECPoint);

                var parameters = new ECParameters
                {
                    Curve = _curve,
                    Q = q,
                };

                using var serverECDH = ECDiffieHellman.Create(parameters);

                return _clientECDH.DeriveRawSecretAgreement(serverECDH.PublicKey);
            }

            private static byte[] EncodeECPoint(ECPoint point)
            {
                var q = new byte[1 + point.X.Length + point.Y.Length];
                q[0] = 0x04;
                Buffer.BlockCopy(point.X, 0, q, 1, point.X.Length);
                Buffer.BlockCopy(point.Y, 0, q, point.X.Length + 1, point.Y.Length);

                return q;
            }

            private static ECPoint DecodeECPoint(byte[] q)
            {
                var cordSize = (q.Length - 1) / 2;
                var x = new byte[cordSize];
                var y = new byte[cordSize];
                Buffer.BlockCopy(q, 1, x, 0, x.Length);
                Buffer.BlockCopy(q, cordSize + 1, y, 0, y.Length);

                return new ECPoint { X = x, Y = y };
            }

            protected override void Dispose(bool disposing)
            {
                base.Dispose(disposing);

                if (disposing)
                {
                    _clientECDH.Dispose();
                }
            }
        }
    }
}
#endif
