using System;
using NUnit.Framework;
using OkonaPad.Networking.Binary;

namespace OkonaPad.Tests
{
    [TestFixture]
    public class BinaryProtocolTests
    {
        #region ReadUInt16BigEndian Tests

        [Test]
        public void ReadUInt16BigEndian_ReadsCorrectValue()
        {
            byte[] buffer = { 0x12, 0x34 };
            ushort result = BinaryProtocolHelper.ReadUInt16BigEndian(buffer, 0);
            Assert.AreEqual(0x1234, result);
        }

        [Test]
        public void ReadUInt16BigEndian_WithOffset_ReadsCorrectValue()
        {
            byte[] buffer = { 0x00, 0x00, 0xAB, 0xCD };
            ushort result = BinaryProtocolHelper.ReadUInt16BigEndian(buffer, 2);
            Assert.AreEqual(0xABCD, result);
        }

        [Test]
        public void ReadUInt16BigEndian_MaxValue_ReadsCorrectly()
        {
            byte[] buffer = { 0xFF, 0xFF };
            ushort result = BinaryProtocolHelper.ReadUInt16BigEndian(buffer, 0);
            Assert.AreEqual(ushort.MaxValue, result);
        }

        [Test]
        public void ReadUInt16BigEndian_NullBuffer_ThrowsArgumentException()
        {
            Assert.Throws<ArgumentException>(() => BinaryProtocolHelper.ReadUInt16BigEndian(null, 0));
        }

        [Test]
        public void ReadUInt16BigEndian_BufferTooSmall_ThrowsArgumentException()
        {
            byte[] buffer = { 0x12 };
            Assert.Throws<ArgumentException>(() => BinaryProtocolHelper.ReadUInt16BigEndian(buffer, 0));
        }

        [Test]
        public void ReadUInt16BigEndian_OffsetOutOfBounds_ThrowsArgumentException()
        {
            byte[] buffer = { 0x12, 0x34 };
            Assert.Throws<ArgumentException>(() => BinaryProtocolHelper.ReadUInt16BigEndian(buffer, 1));
        }

        #endregion

        #region ReadInt16BigEndian Tests

        [Test]
        public void ReadInt16BigEndian_PositiveValue_ReadsCorrectly()
        {
            byte[] buffer = { 0x12, 0x34 };
            short result = BinaryProtocolHelper.ReadInt16BigEndian(buffer, 0);
            Assert.AreEqual(0x1234, result);
        }

        [Test]
        public void ReadInt16BigEndian_NegativeValue_ReadsCorrectly()
        {
            // -1 in big-endian is 0xFF, 0xFF
            byte[] buffer = { 0xFF, 0xFF };
            short result = BinaryProtocolHelper.ReadInt16BigEndian(buffer, 0);
            Assert.AreEqual(-1, result);
        }

        [Test]
        public void ReadInt16BigEndian_MinValue_ReadsCorrectly()
        {
            // short.MinValue (-32768) in big-endian is 0x80, 0x00
            byte[] buffer = { 0x80, 0x00 };
            short result = BinaryProtocolHelper.ReadInt16BigEndian(buffer, 0);
            Assert.AreEqual(short.MinValue, result);
        }

        [Test]
        public void ReadInt16BigEndian_MaxValue_ReadsCorrectly()
        {
            // short.MaxValue (32767) in big-endian is 0x7F, 0xFF
            byte[] buffer = { 0x7F, 0xFF };
            short result = BinaryProtocolHelper.ReadInt16BigEndian(buffer, 0);
            Assert.AreEqual(short.MaxValue, result);
        }

        [Test]
        public void ReadInt16BigEndian_NullBuffer_ThrowsArgumentException()
        {
            Assert.Throws<ArgumentException>(() => BinaryProtocolHelper.ReadInt16BigEndian(null, 0));
        }

        #endregion

        #region WriteUInt16BigEndian Tests

        [Test]
        public void WriteUInt16BigEndian_WritesCorrectBytes()
        {
            byte[] buffer = new byte[2];
            BinaryProtocolHelper.WriteUInt16BigEndian(buffer, 0, 0x1234);
            Assert.AreEqual(0x12, buffer[0]);
            Assert.AreEqual(0x34, buffer[1]);
        }

        [Test]
        public void WriteUInt16BigEndian_WithOffset_WritesCorrectBytes()
        {
            byte[] buffer = new byte[4];
            BinaryProtocolHelper.WriteUInt16BigEndian(buffer, 2, 0xABCD);
            Assert.AreEqual(0x00, buffer[0]);
            Assert.AreEqual(0x00, buffer[1]);
            Assert.AreEqual(0xAB, buffer[2]);
            Assert.AreEqual(0xCD, buffer[3]);
        }

        [Test]
        public void WriteUInt16BigEndian_MaxValue_WritesCorrectBytes()
        {
            byte[] buffer = new byte[2];
            BinaryProtocolHelper.WriteUInt16BigEndian(buffer, 0, ushort.MaxValue);
            Assert.AreEqual(0xFF, buffer[0]);
            Assert.AreEqual(0xFF, buffer[1]);
        }

        [Test]
        public void WriteUInt16BigEndian_NullBuffer_ThrowsArgumentException()
        {
            Assert.Throws<ArgumentException>(() => BinaryProtocolHelper.WriteUInt16BigEndian(null, 0, 0x1234));
        }

        [Test]
        public void WriteUInt16BigEndian_BufferTooSmall_ThrowsArgumentException()
        {
            byte[] buffer = new byte[1];
            Assert.Throws<ArgumentException>(() => BinaryProtocolHelper.WriteUInt16BigEndian(buffer, 0, 0x1234));
        }

        #endregion

        #region JoystickInt16ToFloat Tests

        [Test]
        public void JoystickInt16ToFloat_Zero_ReturnsZero()
        {
            float result = BinaryProtocolHelper.JoystickInt16ToFloat(0);
            Assert.AreEqual(0f, result, 0.0001f);
        }

        [Test]
        public void JoystickInt16ToFloat_MaxValue_ReturnsOne()
        {
            // 32767 should map to approximately 1.0
            float result = BinaryProtocolHelper.JoystickInt16ToFloat(32767);
            Assert.AreEqual(1f, result, 0.0001f);
        }

        [Test]
        public void JoystickInt16ToFloat_MinValue_ReturnsNegativeOne()
        {
            // -32767 should map to approximately -1.0
            float result = BinaryProtocolHelper.JoystickInt16ToFloat(-32767);
            Assert.AreEqual(-1f, result, 0.0001f);
        }

        [Test]
        public void JoystickInt16ToFloat_HalfValue_ReturnsHalf()
        {
            // 16383 should map to approximately 0.5
            float result = BinaryProtocolHelper.JoystickInt16ToFloat(16383);
            Assert.AreEqual(0.5f, result, 0.001f);
        }

        #endregion

        #region CreateJoinMessage Tests

        [Test]
        public void CreateJoinMessage_ReturnsCorrectFormat()
        {
            byte[] message = BinaryProtocolHelper.CreateJoinMessage();

            Assert.AreEqual(3, message.Length);
            // Length field (big-endian) should be 1 (just the type byte)
            Assert.AreEqual(0x00, message[0]);
            Assert.AreEqual(0x01, message[1]);
            // Message type should be Join (3)
            Assert.AreEqual((byte)MessageType.Join, message[2]);
        }

        #endregion

        #region CreateRumbleMessage Tests

        [Test]
        public void CreateRumbleMessage_ReturnsCorrectFormat()
        {
            byte[] message = BinaryProtocolHelper.CreateRumbleMessage(500);

            Assert.AreEqual(5, message.Length);
            // Length field (big-endian) should be 3 (type + 2 byte payload)
            Assert.AreEqual(0x00, message[0]);
            Assert.AreEqual(0x03, message[1]);
            // Message type should be Rumble (5)
            Assert.AreEqual((byte)MessageType.Rumble, message[2]);
            // Duration (500 = 0x01F4)
            Assert.AreEqual(0x01, message[3]);
            Assert.AreEqual(0xF4, message[4]);
        }

        [Test]
        public void CreateRumbleMessage_ClampsToMaxUshort()
        {
            // Duration larger than ushort.MaxValue should be clamped
            byte[] message = BinaryProtocolHelper.CreateRumbleMessage(100000);

            // Should be clamped to 65535 (0xFFFF)
            Assert.AreEqual(0xFF, message[3]);
            Assert.AreEqual(0xFF, message[4]);
        }

        [Test]
        public void CreateRumbleMessage_ZeroDuration()
        {
            byte[] message = BinaryProtocolHelper.CreateRumbleMessage(0);

            Assert.AreEqual(0x00, message[3]);
            Assert.AreEqual(0x00, message[4]);
        }

        #endregion

        #region Enum Value Tests

        [Test]
        public void MessageType_HasExpectedValues()
        {
            Assert.AreEqual(0, (byte)MessageType.Button);
            Assert.AreEqual(1, (byte)MessageType.Dpad);
            Assert.AreEqual(2, (byte)MessageType.Joystick);
            Assert.AreEqual(3, (byte)MessageType.Join);
            Assert.AreEqual(4, (byte)MessageType.Pong);
            Assert.AreEqual(5, (byte)MessageType.Rumble);
        }

        [Test]
        public void ButtonCode_HasExpectedValues()
        {
            Assert.AreEqual(0, (byte)ButtonCode.A);
            Assert.AreEqual(1, (byte)ButtonCode.B);
            Assert.AreEqual(2, (byte)ButtonCode.X);
            Assert.AreEqual(3, (byte)ButtonCode.Y);
            Assert.AreEqual(4, (byte)ButtonCode.Start);
            Assert.AreEqual(5, (byte)ButtonCode.Select);
        }

        [Test]
        public void DpadDirection_HasExpectedValues()
        {
            Assert.AreEqual(0, (byte)DpadDirection.Up);
            Assert.AreEqual(1, (byte)DpadDirection.Down);
            Assert.AreEqual(2, (byte)DpadDirection.Left);
            Assert.AreEqual(3, (byte)DpadDirection.Right);
        }

        #endregion
    }
}
