/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.IO;
using System.Text;

namespace Avro.IO
{
    /// <content>
    /// Contains the netstandard2.0 specific functionality for BinaryDecoder.
    /// </content>
    public partial class BinaryDecoder
    {
        /// <summary>
        /// It is hard to find documentation about the real maximum array length in .NET Framework 4.6.1, but this seems to work :-/
        /// </summary>
        private const int MaxDotNetArrayLength = 0x3FFFFFFF;

        /// <summary>
        /// A float is written as 4 bytes.
        /// The float is converted into a 32-bit integer using a method equivalent to
        /// Java's floatToRawIntBits and then encoded in little-endian format.
        /// </summary>
        /// <returns></returns>
        public float ReadFloat()
        {
            byte[] buffer = read(4);

            if (!BitConverter.IsLittleEndian)
                Array.Reverse(buffer);

            return BitConverter.ToSingle(buffer, 0);

            //int bits = (Stream.ReadByte() & 0xff |
            //(Stream.ReadByte()) & 0xff << 8 |
            //(Stream.ReadByte()) & 0xff << 16 |
            //(Stream.ReadByte()) & 0xff << 24);
            //return intBitsToFloat(bits);
        }

        /// <summary>
        /// A double is written as 8 bytes.
        /// The double is converted into a 64-bit integer using a method equivalent to
        /// Java's doubleToRawLongBits and then encoded in little-endian format.
        /// </summary>
        /// <returns>A double value.</returns>
        public double ReadDouble()
        {
            long bits = (stream.ReadByte() & 0xffL) |
              (stream.ReadByte() & 0xffL) << 8 |
              (stream.ReadByte() & 0xffL) << 16 |
              (stream.ReadByte() & 0xffL) << 24 |
              (stream.ReadByte() & 0xffL) << 32 |
              (stream.ReadByte() & 0xffL) << 40 |
              (stream.ReadByte() & 0xffL) << 48 |
              (stream.ReadByte() & 0xffL) << 56;
            return BitConverter.Int64BitsToDouble(bits);
        }

        /// <summary>
        /// Reads a string written by <see cref="BinaryEncoder.WriteString(string)"/>.
        /// </summary>
        /// <returns>String read from the stream.</returns>
        public string ReadString()
        {
            int length = ReadInt();

            if (length < 0)
            {
                throw new AvroException("Can not deserialize a string with negative length!");
            }

            if (length > MaxDotNetArrayLength)
            {
                throw new AvroException("String length is not supported!");
            }

            using (var binaryReader = new BinaryReader(stream, Encoding.UTF8, true))
            {
                var bytes = binaryReader.ReadBytes(length);

                if (bytes.Length != length)
                {
                    throw new AvroException("Could not read as many bytes from stream as expected!");
                }

                return Encoding.UTF8.GetString(bytes);
            }
        }

        private void Read(byte[] buffer, int start, int len)
        {
            while (len > 0)
            {
                int n = stream.Read(buffer, start, len);
                if (n <= 0) throw new AvroException("End of stream reached");
                start += n;
                len -= n;
            }
        }
    }
}
