using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO;

namespace skyscraper5.Rds
{
    class RdsMessageStream : Stream
    {
        private readonly Stream _wrapped;

        public RdsMessageStream(Stream wrapped)
        {
            _wrapped = wrapped;
        }

        public override void Flush()
        {
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            byte[] inputByte = new byte[1];
            int result = 0;
            for (int i = 0; i < count; i++)
            {
                if (_wrapped.Read(inputByte, 0, 1) != 1)
                    throw new IOException("failed to read byte");
                UpdateCrc(inputByte[0]);
                if (inputByte[0] == 0xfd)
                {
                    if (_wrapped.Read(inputByte, 0, 1) != 1)
                        throw new IOException("failed to read byte");
                    UpdateCrc(inputByte[0]);
                    if (inputByte[0] == 0x00)
                        buffer[offset++] = 0xfd;
                    else if (inputByte[0] == 0x01)
                        buffer[offset++] = 0xfe;
                    else if (inputByte[0] == 0x02)
                        buffer[offset++] = 0xff;
                    else
                        throw new RdsException("invalid rds message");
                    result++;
                }
                else
                {
                    buffer[offset++] = inputByte[0];
                    result++;
                }
            }
            return result;
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        public override bool CanRead => true;
        public override bool CanSeek => false;
        public override bool CanWrite => false;
        public override long Length => _wrapped.Length;

        public override long Position
        {
            get => _wrapped.Position;
            set => throw new NotSupportedException();
        }

        private bool crcEnabled;
        private ushort tempCrc;
        public void BeginCrcCalculation()
        {
            if (crcEnabled)
                throw new InvalidOperationException("crc already running");
            crcEnabled = true;
            tempCrc = 0xffff;
        }

        public void EndCrcCalculation()
        {
            if (!crcEnabled)
                throw new InvalidOperationException("crc not running");
            crcEnabled = false;
            tempCrc = (ushort)((tempCrc ^ 0xFFFF) & 0xFFFF);
        }

        private void UpdateCrc(byte value)
        {
            if (!crcEnabled)
                return;

            tempCrc = (ushort)((((tempCrc >> 8) & 0xFF) | (tempCrc << 8)) & 0xFFFF);
            tempCrc ^= value;
            tempCrc ^= (ushort)(((tempCrc & 0xff) >> 4) & 0xFFFF);
            tempCrc ^= (ushort)(((tempCrc << 8) << 4) & 0xFFFF);
            tempCrc ^= (ushort)((((tempCrc & 0xff) << 4) << 1) & 0xFFFF);
        }

        public bool ValidateCrc(byte l, byte r)
        {
            return ValidateCrc((ushort)((l << 8) + r));
        }

        public bool ValidateCrc(ushort crc)
        {
            return crc == tempCrc;
        }
    }
}
