using System;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

namespace Orbiter.Multiplayer
{
    public partial class Version
    {
        public Version()
        {
            Tag = tag; //Invoke setter to synchronize inner version with generated partial constants
        }

        private Version(string tag, int tagDistance, int revision, string hash, string branch)
        {
            Tag = tag;
            TagDistance = tagDistance;
            Revision = revision;
            Hash = hash;
            Branch = branch;
        }

        public override string ToString()
        {
            return TagDistance > 0
                ? Tag + '+' + TagDistance + " ([" + Branch + ']' + Revision + ':' + Hash.Substring(0, 12) + ')'
                : Tag;
        }

        public static Version Parse(string version)
        {
            var re = Regex.Match(version, @"^([^+]*)(?:\+(\d*)(?:\ \(\[([^\]]*)(?:\](\d*)(?:\:([^\)]*)(?:\))?)?)?)?)?$");
            if (re.Groups.Count < 6) return new Version("", 0, 0, null, null);
            if (re.Groups[5].Success) return new Version(re.Groups[1].ToString(), int.Parse(re.Groups[2].ToString()), int.Parse(re.Groups[4].ToString()), re.Groups[5].ToString(), re.Groups[3].ToString());
            if (re.Groups[4].Success) return new Version(re.Groups[1].ToString(), int.Parse(re.Groups[2].ToString()), int.Parse(re.Groups[4].ToString()), null, re.Groups[3].ToString());
            if (re.Groups[3].Success) return new Version(re.Groups[1].ToString(), int.Parse(re.Groups[2].ToString()), 0, null, re.Groups[3].ToString());
            if (re.Groups[2].Success) return new Version(re.Groups[1].ToString(), int.Parse(re.Groups[2].ToString()), 0, null, null);
            if (re.Groups[1].Success) return new Version(re.Groups[1].ToString(), 0, 0, null, null);
            return new Version("", 0, 0, null, null);
        }

        public static bool operator <(Version a, Version b)
        {
            if (a.version < b.version) return true;
            if (a.version == b.version && a.tagDistance < b.tagDistance) return true;
            return false;
        }

        public static bool operator >(Version a, Version b)
        {
            if (a.version > b.version) return true;
            if (a.version == b.version && a.tagDistance > b.tagDistance) return true;
            return false;
        }

        public string Tag
        {
            get { return tag; }
            set
            {
                tag = value;
                try { version = new System.Version(tag); }
                catch { version = new System.Version(0, 0, 0, 0); }
            }
        }

        public int TagDistance
        {
            get { return tagDistance; }
            set { tagDistance = value; }
        }

        private int Revision
        {
            get { return revision; }
            set { revision = value; }
        }

        private string Hash
        {
            get { return hash; }
            set
            {
                if (value == null || value.Length < 12) value = "invalidhash!";
                hash = value;
            }
        }

        private string Branch
        {
            get { return branch; }
            set { branch = value; }
        }

        private System.Version version;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class VectorPacket
    {
        public double X;
        public double Y;
        public double Z;
    }
    /// <summary>
    /// The state information packet.
    /// </summary>
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class StateInfoPacket
    {
        public byte Flags;
        public byte LogarithmicScale;
        public UInt16 SpaceReference;
        public UInt32 Id;
        public double MJD;
        public double Sent;
        public VectorPacket Position;
        public VectorPacket Velocity;
        public VectorPacket Acceleration;
        public VectorPacket RotationalAttitude;
        public VectorPacket RotationalVelocity;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class KeepAlivePacket
    {
        public byte Flags;
        public byte IsGlobalIdForLocalId;
        public UInt16 Reserve;
        public UInt16 GlobalId;
        public UInt16 LocalId;
        public double MJD;
        public double Sent;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class GroupInfoPacket
    {
        public byte Flags;
        public byte Reserve;
        public UInt16 Source;
        public UInt16 Reserve1;
        public UInt16 Port;
        public UInt64 Ip;
        public double MJD;
        public double Sent;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class PingPacket
    {
        public byte Flags;
        public byte Reserve;
        public UInt16 Id;
        public UInt16 Id2;
        public UInt16 Id3;
        public double Sent;
    }

    [Flags]
    public enum PacketType
    {
        KeepAlive,
        GroupInfo,
        StateInfo,
        EventInfo,
        Audio,
        Video,
        Ping,
        Mask,
    }

    /// <summary>
    /// The mandatory part of an NTP packet.
    /// </summary>
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NTPBasicInfo
    {
        public byte VersionMode;
        public byte Stratum;
        public byte Poll;
        public byte Precision;
        public int RootDelay;
        public int RootDispersion;
        public byte ReferenceID0;
        public byte ReferenceID1;
        public byte ReferenceID2;
        public byte ReferenceID3;
        public NTPTimePacket ReferenceTimestamp;
        public NTPTimePacket OriginateTimestamp;
        public NTPTimePacket ReceiveTimestamp;
        public NTPTimePacket TransmitTimestamp;
    }

    /// <summary>
    /// The optional NTP packet part.
    /// </summary>
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NTPAuthenticationInfo
    {
        public uint KeyID;
        public byte MessageDigest0;
        public byte MessageDigest1;
        public byte MessageDigest2;
        public byte MessageDigest3;
        public byte MessageDigest4;
        public byte MessageDigest5;
        public byte MessageDigest6;
        public byte MessageDigest7;
        public byte MessageDigest8;
        public byte MessageDigest9;
        public byte MessageDigest10;
        public byte MessageDigest11;
        public byte MessageDigest12;
        public byte MessageDigest13;
        public byte MessageDigest14;
        public byte MessageDigest15;
    }

    /// <summary>
    /// The full NTP packet.
    /// </summary>
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NTPFullPacket
    {
        public NTPBasicInfo Basic;
        public NTPAuthenticationInfo Auth;
    }

    /// <summary>
    /// Helper structure to convert host vs. network byte-order.
    /// </summary>
    [StructLayout(LayoutKind.Explicit)]
    public struct HostVsNetwork
    {
        [FieldOffset(0)]
        public ulong HostUInt64;
        [FieldOffset(0)]
        public uint HostUInt32;
        [FieldOffset(0)]
        public byte HostByte0;
        [FieldOffset(1)]
        public byte HostByte1;
        [FieldOffset(2)]
        public byte HostByte2;
        [FieldOffset(3)]
        public byte HostByte3;
        [FieldOffset(4)]
        public byte HostByte4;
        [FieldOffset(5)]
        public byte HostByte5;
        [FieldOffset(6)]
        public byte HostByte6;
        [FieldOffset(7)]
        public byte HostByte7;
        [FieldOffset(8)]
        public ulong NetworkUInt64;
        [FieldOffset(8)]
        public uint NetworkUInt32;
        [FieldOffset(8)]
        public byte NetworkByte0;
        [FieldOffset(9)]
        public byte NetworkByte1;
        [FieldOffset(10)]
        public byte NetworkByte2;
        [FieldOffset(11)]
        public byte NetworkByte3;
        [FieldOffset(12)]
        public byte NetworkByte4;
        [FieldOffset(13)]
        public byte NetworkByte5;
        [FieldOffset(14)]
        public byte NetworkByte6;
        [FieldOffset(15)]
        public byte NetworkByte7;

        /// <summary>
        /// Host to network.
        /// </summary>
        /// <param name="count">The byte count.</param>
        public void HostToNetwork(byte count)
        {
            switch (count)
            {
                case 1:
                    NetworkByte0 = HostByte0;
                    break;
                case 2:
                    NetworkByte0 = HostByte1;
                    NetworkByte1 = HostByte0;
                    break;
                case 4:
                    NetworkByte0 = HostByte3;
                    NetworkByte1 = HostByte2;
                    NetworkByte2 = HostByte1;
                    NetworkByte3 = HostByte0;
                    break;
                case 8:
                    NetworkByte0 = HostByte7;
                    NetworkByte1 = HostByte6;
                    NetworkByte2 = HostByte5;
                    NetworkByte3 = HostByte4;
                    NetworkByte4 = HostByte3;
                    NetworkByte5 = HostByte2;
                    NetworkByte6 = HostByte1;
                    NetworkByte7 = HostByte0;
                    break;
                default:
                    throw new ArgumentException("Count must be 1,2,4 or 8!");
            }
        }
    }

    /// <summary>
    /// NTP time packet structure.
    /// </summary>
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NTPTimePacket
    {
        public uint Integer;
        public uint Fraction;

        /// <summary>
        /// Initializes a new instance of the <see cref="NTPTimePacket"/> struct.
        /// </summary>
        /// <param name="integer">The integer.</param>
        /// <param name="fraction">The fraction.</param>
        public NTPTimePacket(uint integer, uint fraction)
        {
            Integer = integer;
            Fraction = fraction;
        }
    }
}
