using System;
using System.IO;
using System.IO.Compression;

namespace Quasar.Common.Video
{
    /// <summary>
    /// Shared constants and helpers for the UDP frame transport.
    /// 
    /// Packet layout (bytes):
    ///   [0..3]  frameId      (uint32, little-endian)
    ///   [4..5]  chunkIndex   (uint16, little-endian)
    ///   [6..7]  totalChunks  (uint16, little-endian)
    ///   [8..9]  flags        (uint16) bit0=isKeyFrame, bit1=isCompressed
    ///   [10..]  payload
    /// </summary>
    public static class UdpFrameProtocol
    {
        public const int HeaderSize = 10;
        public const int MaxPayloadSize = 1300; // safe under typical MTU of 1400
        public const int MaxPacketSize = HeaderSize + MaxPayloadSize;

        public const ushort FlagKeyFrame   = 0x0001;
        public const ushort FlagCompressed = 0x0002;

        public static byte[] BuildPacket(uint frameId, ushort chunkIndex, ushort totalChunks, ushort flags, byte[] payload, int offset, int length)
        {
            byte[] packet = new byte[HeaderSize + length];
            Buffer.BlockCopy(BitConverter.GetBytes(frameId),    0, packet, 0, 4);
            Buffer.BlockCopy(BitConverter.GetBytes(chunkIndex), 0, packet, 4, 2);
            Buffer.BlockCopy(BitConverter.GetBytes(totalChunks),0, packet, 6, 2);
            Buffer.BlockCopy(BitConverter.GetBytes(flags),      0, packet, 8, 2);
            Buffer.BlockCopy(payload, offset, packet, HeaderSize, length);
            return packet;
        }

        public static void ParseHeader(byte[] packet, out uint frameId, out ushort chunkIndex, out ushort totalChunks, out ushort flags)
        {
            frameId     = BitConverter.ToUInt32(packet, 0);
            chunkIndex  = BitConverter.ToUInt16(packet, 4);
            totalChunks = BitConverter.ToUInt16(packet, 6);
            flags       = BitConverter.ToUInt16(packet, 8);
        }

        /// <summary>Compress bytes with DeflateStream.</summary>
        public static byte[] Compress(byte[] data)
        {
            using (var ms = new MemoryStream())
            {
                using (var ds = new DeflateStream(ms, CompressionMode.Compress, true))
                    ds.Write(data, 0, data.Length);
                return ms.ToArray();
            }
        }

        /// <summary>Decompress bytes with DeflateStream.</summary>
        public static byte[] Decompress(byte[] data)
        {
            using (var input = new MemoryStream(data))
            using (var ds = new DeflateStream(input, CompressionMode.Decompress))
            using (var output = new MemoryStream())
            {
                ds.CopyTo(output);
                return output.ToArray();
            }
        }
    }
}
