using AX.Network.Common;
using AX.Network.Protocols;
using AX.Serialization;
using System;
using System.IO;

namespace AX.Serialization
{
    public static partial class SerializationExtensions
    {
        public static T Deserialize<T>(this BufferSegment segment)
        {
            using (var stream = new BufferStream(segment))
            {
                return Deserialize<T>(stream);
            }
        }
    }
}

namespace AX.NetworkSystem
{
    public enum MessageType : byte
    {
        Sync = 0,
        Request = 1,
        Reply = 2
    }

    public static class BinaryProtocolExtensions
    {
        private const int MessageLength = 4;
        private const int IDLength = 8;
        private const int MessageTypeLength = 1;
        private const int HeaderLength = 1;

        private static byte[] StringBuffer = new byte[256];

        public static void Write(this BinaryProtocol protocol, Stream stream, long id, MessageType messageType, string header)
        {
            var encoding = BinaryProtocol.TextEncoding;

            int messageLength;
            int headerLength;

            headerLength = encoding.GetByteCount(header);

            if (headerLength > byte.MaxValue)
                throw new InvalidOperationException("不同编码格式的字符串转换后的字节数组长度不能大于" + byte.MaxValue + "!");

            messageLength = MessageLength + IDLength + MessageTypeLength + HeaderLength + headerLength;

            if (BitConverter.IsLittleEndian)
            {
                messageLength = Endian.SwapInt32(messageLength);
                id = Endian.SwapInt64(id);
            }

            encoding.GetBytes(header, 0, header.Length, StringBuffer, 0);

            using (var writer = new BinaryWriter(stream, encoding, true))
            {
                writer.Write(messageLength);
                writer.Write(id);
                writer.Write((byte)messageType);
                writer.Write((byte)headerLength);
                writer.Write(StringBuffer, 0, headerLength);
            }
        }

        public static void Write<T>(this BinaryProtocol protocol, Stream stream, long id, MessageType messageType, string header, T body)
        {
            var encoding = BinaryProtocol.TextEncoding;

            int messageLength;
            int headerLength;

            headerLength = encoding.GetByteCount(header);

            if (headerLength > byte.MaxValue)
                throw new InvalidOperationException("不同编码格式的字符串转换后的字节数组长度不能大于" + byte.MaxValue + "!");

            if (BitConverter.IsLittleEndian)
                id = Endian.SwapInt64(id);

            encoding.GetBytes(header, 0, header.Length, StringBuffer, 0);

            using (var writer = new BinaryWriter(stream, encoding, true))
            {
                writer.Write(MessageLength);
                writer.Write(id);
                writer.Write((byte)messageType);
                writer.Write((byte)headerLength);
                writer.Write(StringBuffer, 0, headerLength);

                body.Serialize(stream);

                messageLength = (int)stream.Length;

                if (BitConverter.IsLittleEndian)
                    messageLength = Endian.SwapInt32(messageLength);

                stream.Seek(0, SeekOrigin.Begin);

                writer.Write(messageLength);
            }
        }
    }

    public static class SessionExtensions
    {
        private static MemoryStream stream = new MemoryStream(8192);

        public static bool SendAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string header)
        {
            return SendAsync(session, -1, MessageType.Sync, header);
        }

        public static bool SendAsync<T>(this IAppSession<BinaryProtocol, BinaryMessage> session, string header, T body)
        {
            return SendAsync<T>(session, -1, MessageType.Sync, header, body);
        }

        public static bool SendAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, MessageType messageType, string header)
        {
            return SendAsync(session, -1, messageType, header);
        }

        public static bool SendAsync<T>(this IAppSession<BinaryProtocol, BinaryMessage> session, MessageType messageType, string header, T body)
        {
            return SendAsync<T>(session, -1, messageType, header, body);
        }

        public static bool SendAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, long id, string header)
        {
            return SendAsync(session, id, MessageType.Sync, header);
        }

        public static bool SendAsync<T>(this IAppSession<BinaryProtocol, BinaryMessage> session, long id, string header, T body)
        {
            return SendAsync<T>(session, id, MessageType.Sync, header, body);
        }

        public static bool SendAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, long id, MessageType messageType, string header)
        {
            stream.Position = 0;
            stream.SetLength(0);

            var protocol = session.Protocol;

            protocol.Write(stream, id, messageType, header);

            var buffer = stream.GetBuffer();

            return session.SendAsync(buffer, 0, (int)stream.Length);
        }

        public static bool SendAsync<T>(this IAppSession<BinaryProtocol, BinaryMessage> session, long id, MessageType messageType, string header, T body)
        {
            stream.Position = 0;
            stream.SetLength(0);

            var protocol = session.Protocol;

            protocol.Write(stream, id, messageType, header, body);

            var buffer = stream.GetBuffer();

            return session.SendAsync(buffer, 0, (int)stream.Length);
        }

        public static bool SendRequestAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string header)
        {
            return SendAsync(session, -1, MessageType.Request, header);
        }

        public static bool SendRequestAsync<T>(this IAppSession<BinaryProtocol, BinaryMessage> session, string header, T body)
        {
            return SendAsync<T>(session, -1, MessageType.Request, header, body);
        }

        public static bool SendRequestAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, long id, string header)
        {
            return SendAsync(session, id, MessageType.Request, header);
        }

        public static bool SendRequestAsync<T>(this IAppSession<BinaryProtocol, BinaryMessage> session, long id, string header, T body)
        {
            return SendAsync<T>(session, id, MessageType.Request, header, body);
        }
    }
}