// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT License.

// <auto-generated/>

#nullable disable

using System;
using System.ClientModel;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;

namespace SampleTypeSpec
{
    internal partial class MultiPartFormDataBinaryContent : BinaryContent
    {
        private readonly MultipartFormDataContent _multipartContent;
        private static readonly Random _random = new Random();
        private static readonly char[] _boundaryValues = "0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".ToCharArray();

        public MultiPartFormDataBinaryContent()
        {
            _multipartContent = new MultipartFormDataContent(CreateBoundary());
        }

        /// <summary> Gets the ContentType. </summary>
        public string ContentType
        {
            get
            {
                return _multipartContent.Headers.ContentType.ToString();
            }
        }

        /// <summary> Gets the HttpContent. </summary>
        internal HttpContent HttpContent => _multipartContent;

        private static string CreateBoundary()
        {
            Span<char> chars = new char[70];
            byte[] random = new byte[70];
            _random.NextBytes(random);
            int mask = 255 >> 2;
            int i = 0;
            for (; i < 70; i++)
            {
                chars[i] = _boundaryValues[random[i] & mask];
            }
            return chars.ToString();
        }

        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <param name="filename"></param>
        /// <param name="contentType"></param>
        public void Add(string content, string name, string filename = default, string contentType = default)
        {
            Argument.AssertNotNull(content, nameof(content));
            Argument.AssertNotNullOrEmpty(name, nameof(name));

            Add(new StringContent(content), name, filename, contentType);
        }

        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <param name="filename"></param>
        /// <param name="contentType"></param>
        public void Add(int content, string name, string filename = default, string contentType = default)
        {
            Argument.AssertNotNull(content, nameof(content));
            Argument.AssertNotNullOrEmpty(name, nameof(name));

            string value = content.ToString("G", CultureInfo.InvariantCulture);
            Add(new StringContent(value), name, filename, contentType);
        }

        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <param name="filename"></param>
        /// <param name="contentType"></param>
        public void Add(long content, string name, string filename = default, string contentType = default)
        {
            Argument.AssertNotNull(content, nameof(content));
            Argument.AssertNotNullOrEmpty(name, nameof(name));

            string value = content.ToString("G", CultureInfo.InvariantCulture);
            Add(new StringContent(value), name, filename, contentType);
        }

        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <param name="filename"></param>
        /// <param name="contentType"></param>
        public void Add(float content, string name, string filename = default, string contentType = default)
        {
            Argument.AssertNotNull(content, nameof(content));
            Argument.AssertNotNullOrEmpty(name, nameof(name));

            string value = content.ToString("G", CultureInfo.InvariantCulture);
            Add(new StringContent(value), name, filename, contentType);
        }

        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <param name="filename"></param>
        /// <param name="contentType"></param>
        public void Add(double content, string name, string filename = default, string contentType = default)
        {
            Argument.AssertNotNull(content, nameof(content));
            Argument.AssertNotNullOrEmpty(name, nameof(name));

            string value = content.ToString("G", CultureInfo.InvariantCulture);
            Add(new StringContent(value), name, filename, contentType);
        }

        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <param name="filename"></param>
        /// <param name="contentType"></param>
        public void Add(decimal content, string name, string filename = default, string contentType = default)
        {
            Argument.AssertNotNull(content, nameof(content));
            Argument.AssertNotNullOrEmpty(name, nameof(name));

            string value = content.ToString("G", CultureInfo.InvariantCulture);
            Add(new StringContent(value), name, filename, contentType);
        }

        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <param name="filename"></param>
        /// <param name="contentType"></param>
        public void Add(bool content, string name, string filename = default, string contentType = default)
        {
            Argument.AssertNotNull(content, nameof(content));
            Argument.AssertNotNullOrEmpty(name, nameof(name));

            string value = content ? "true" : "false";
            Add(new StringContent(value), name, filename, contentType);
        }

        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <param name="filename"></param>
        /// <param name="contentType"></param>
        public void Add(Stream content, string name, string filename = default, string contentType = default)
        {
            Argument.AssertNotNull(content, nameof(content));
            Argument.AssertNotNullOrEmpty(name, nameof(name));

            Add(new StreamContent(content), name, filename, contentType);
        }

        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <param name="filename"></param>
        /// <param name="contentType"></param>
        public void Add(byte[] content, string name, string filename = default, string contentType = default)
        {
            Argument.AssertNotNull(content, nameof(content));
            Argument.AssertNotNullOrEmpty(name, nameof(name));

            Add(new ByteArrayContent(content), name, filename, contentType);
        }

        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <param name="filename"></param>
        /// <param name="contentType"></param>
        public void Add(BinaryData content, string name, string filename = default, string contentType = default)
        {
            Argument.AssertNotNull(content, nameof(content));
            Argument.AssertNotNullOrEmpty(name, nameof(name));

            Add(new ByteArrayContent(content.ToArray()), name, filename, contentType);
        }

        /// <param name="content"></param>
        /// <param name="name"></param>
        /// <param name="filename"></param>
        /// <param name="contentType"></param>
        private void Add(HttpContent content, string name, string filename, string contentType)
        {
            if (contentType != null)
            {
                Argument.AssertNotNullOrEmpty(contentType, nameof(contentType));
                AddContentTypeHeader(content, contentType);
            }
            if (filename != null)
            {
                Argument.AssertNotNullOrEmpty(filename, nameof(filename));
                _multipartContent.Add(content, name, filename);
            }
            else
            {
                _multipartContent.Add(content, name);
            }
        }

        /// <param name="content"></param>
        /// <param name="contentType"></param>
        public static void AddContentTypeHeader(HttpContent content, string contentType)
        {
            MediaTypeHeaderValue header = new MediaTypeHeaderValue(contentType);
            content.Headers.ContentType = header;
        }

        /// <param name="length"></param>
        public override bool TryComputeLength(out long length)
        {
            if (_multipartContent.Headers.ContentLength is long contentLength)
            {
                length = contentLength;
                return true;
            }
            length = 0;
            return false;
        }

        /// <param name="stream"></param>
        /// <param name="cancellationToken"> The cancellation token to use. </param>
        public override void WriteTo(Stream stream, CancellationToken cancellationToken = default)
        {
#if NET6_0_OR_GREATER
            _multipartContent.CopyTo(stream, default, cancellationToken);
#else
            _multipartContent.CopyToAsync(stream).GetAwaiter().GetResult();
#endif
        }

        /// <param name="stream"></param>
        /// <param name="cancellationToken"> The cancellation token to use. </param>
        public override async Task WriteToAsync(Stream stream, CancellationToken cancellationToken = default)
        {
#if NET6_0_OR_GREATER
            await _multipartContent.CopyToAsync(stream, cancellationToken).ConfigureAwait(false);
#else
            await _multipartContent.CopyToAsync(stream).ConfigureAwait(false);
#endif
        }

        public override void Dispose()
        {
            _multipartContent.Dispose();
        }
    }
}
