/*
* 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.
*/

namespace Apache.Qpid.Test.Channel.WcfPerftest
{
    using System;
    using System.Collections;
    using System.IO;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.Threading;
    using System.Text;
    using System.Xml;
    using Apache.Qpid.Channel;


    /// <summary>
    /// A sample interface for populating and extracting message body content.
    /// Just enough methods to handle basic Interop text and raw byte messages.
    /// </summary>
 
    
    public interface IRawBodyUtility
    {
        Message CreateMessage(byte[] body, int offset, int len);
        Message CreateMessage(byte[] body);
        byte[] GetBytes(Message m, byte[] recyclableBuffer);

        Message CreateMessage(string body);
        string GetText(Message m);
    }

    // an implementation of IRawBodyUtility that expects a RawMessageEncoder based channel

    public class RawEncoderUtility : IRawBodyUtility
    {
        public Message CreateMessage(byte[] body, int offset, int count)
        {
            return Message.CreateMessage(MessageVersion.None, "", new RawEncoderBodyWriter(body, offset, count));
        }

        public Message CreateMessage(byte[] body)
        {
            return CreateMessage(body, 0, body.Length);
        }

        public byte[] GetBytes(Message message, byte[] recyclableBuffer)
        {
            XmlDictionaryReader reader = message.GetReaderAtBodyContents();
            int length;

            while (!reader.HasValue)
            {
                reader.Read();
                if (reader.EOF)
                {
                    throw new InvalidDataException("empty XmlDictionaryReader");
                }
            }

            if (reader.TryGetBase64ContentLength(out length))
            {
                byte[] bytes = null;
                if (recyclableBuffer != null)
                {
                    if (recyclableBuffer.Length == length)
                    {
                        // reuse
                        bytes = recyclableBuffer;
                    }
                }

                if (bytes == null)
                {
                    bytes = new byte[length];
                }

                // this is the single copy mechanism from native to managed space with no intervening
                // buffers.  One could also write a method GetBytes(msg, myBuf, offset)...
                reader.ReadContentAsBase64(bytes, 0, length);
                reader.Close();
                return bytes;
            }
            else
            {
                // uses whatever default buffering mechanism is used by the base XmlDictionaryReader class
                return reader.ReadContentAsBase64();
            }
        }

        public Message CreateMessage(string body)
        {
            return Message.CreateMessage(MessageVersion.None, "", new RawEncoderBodyWriter(body));
        }

        public string GetText(Message message)
        {
            byte[] rawBuffer = GetBytes(message, null);
            return Encoding.UTF8.GetString(rawBuffer, 0, rawBuffer.Length);
        }

        internal class RawEncoderBodyWriter : BodyWriter
        {
            // works only with the Raw Encoder; the "body" is either a single string or byte[] segment
            String bodyAsString;
            byte[] bodyAsBytes;
            int offset;
            int count;

            public RawEncoderBodyWriter(string body)
                : base(false)               // isBuffered
            {
                this.bodyAsString = body;
            }

            public RawEncoderBodyWriter(byte[] body, int offset, int count)
                : base(false)               // isBuffered
            {
                this.bodyAsBytes = body;
                this.offset = offset;
                this.count = count;
            }

            protected override void OnWriteBodyContents(System.Xml.XmlDictionaryWriter writer)
            {
                // TODO:  RawMessageEncoder.StreamElementName should be public.
                writer.WriteStartElement("Binary");  // the expected Raw encoder "<Binary>" virtual xml tag

                if (bodyAsString != null)
                {
                    byte[] buf = Encoding.UTF8.GetBytes(bodyAsString);
                    writer.WriteBase64(buf, 0, buf.Length);
                }
                else
                {
                    writer.WriteBase64(this.bodyAsBytes, this.offset, this.count);
                }

                writer.WriteEndElement();
            }
        }
    }

}
