// 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
// 
//     http://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.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace Thrift.Transport
{

    abstract public class TEndpointTransport : TTransport
    {
        protected long MaxMessageSize { get => Configuration.MaxMessageSize; }
        protected long KnownMessageSize { get; private set; }
        protected long RemainingMessageSize { get; private set; }

        private readonly TConfiguration _configuration;
        public override TConfiguration Configuration { get => _configuration; }

        public TEndpointTransport( TConfiguration config)
        {
            _configuration = config ?? new TConfiguration();
            Debug.Assert(Configuration != null);

            ResetMessageSizeAndConsumedBytes();
        }

        /// <summary>
        /// Resets RemainingMessageSize to the configured maximum 
        /// </summary>
        public override void ResetMessageSizeAndConsumedBytes(long newSize = -1)
        {
            // full reset 
            if (newSize < 0)
            {
                KnownMessageSize = MaxMessageSize;
                RemainingMessageSize = MaxMessageSize;
                return;
            }

            // update only: message size can shrink, but not grow
            Debug.Assert(KnownMessageSize <= MaxMessageSize);
            if (newSize > KnownMessageSize)
                throw new TTransportException(TTransportException.ExceptionType.MessageSizeLimit, "MaxMessageSize reached");

            KnownMessageSize = newSize;
            RemainingMessageSize = newSize;
        }

        /// <summary>
        /// Updates RemainingMessageSize to reflect then known real message size (e.g. framed transport).
        /// Will throw if we already consumed too many bytes or if the new size is larger than allowed.
        /// </summary>
        /// <param name="size"></param>
        public override void UpdateKnownMessageSize(long size)
        {
            var consumed = KnownMessageSize - RemainingMessageSize;
            ResetMessageSizeAndConsumedBytes(size);
            CountConsumedMessageBytes(consumed);
        }

        /// <summary>
        /// Throws if there are not enough bytes in the input stream to satisfy a read of numBytes bytes of data
        /// </summary>
        /// <param name="numBytes"></param>
        public override void CheckReadBytesAvailable(long numBytes)
        {
            if ((RemainingMessageSize < numBytes) || (numBytes < 0))
                throw new TTransportException(TTransportException.ExceptionType.MessageSizeLimit, "MaxMessageSize reached");
        }

        /// <summary>
        /// Consumes numBytes from the RemainingMessageSize.
        /// </summary>
        /// <param name="numBytes"></param>
        protected void CountConsumedMessageBytes(long numBytes)
        {
            if (RemainingMessageSize >= numBytes)
            {
                RemainingMessageSize -= numBytes;
            }
            else
            {
                RemainingMessageSize = 0;
                throw new TTransportException(TTransportException.ExceptionType.MessageSizeLimit, "MaxMessageSize reached");
            }
        }
    }
}
