// Decompiled with JetBrains decompiler
// Type: Microsoft.Iris.Markup.ByteCodeWriter
// Assembly: UIX, Version=4.8.0.0, Culture=neutral, PublicKeyToken=ddd0da4d3e678217
// MVID: A56C6C9D-B7F6-46A9-8BDE-B3D9B8D60B11
// Assembly location: C:\Program Files\Zune\UIX.dll

using Microsoft.Iris.OS;
using System;
using System.Collections;
using System.Runtime.InteropServices;

namespace Microsoft.Iris.Markup
{
    public class ByteCodeWriter
    {
        private const int BLOCK_SIZE = 4096;
        private byte[] _scratch = new byte[8];
        private uint _cbFreeInBlock;
        private uint _totalSize;
        private byte[] _currentBlock;
        private ArrayList _blockList = new ArrayList();

        public uint DataSize => _totalSize;

        public void WriteByte(byte value)
        {
            _scratch[0] = value;
            Write(_scratch, 1U);
        }

        public void WriteByte(OpCode value)
        {
            _scratch[0] = (byte)value;
            Write(_scratch, 1U);
        }

        public void WriteBool(bool value)
        {
            _scratch[0] = 0;
            if (value) _scratch[0] = 1;
            Write(_scratch, 1U);
        }

        public void WriteChar(char value)
        {
            _scratch[0] = (byte)value;
            _scratch[1] = (byte)((uint)value >> 8);
            Write(_scratch, 2U);
        }

        public void WriteUInt16(ushort value)
        {
            _scratch[0] = (byte)value;
            _scratch[1] = (byte)((uint)value >> 8);
            Write(_scratch, 2U);
        }

        public void WriteUInt16(int rawValue)
        {
            ushort num = (ushort)rawValue;
            _scratch[0] = (byte)num;
            _scratch[1] = (byte)((uint)num >> 8);
            Write(_scratch, 2U);
        }

        public void WriteUInt16(uint rawValue)
        {
            ushort num = (ushort)rawValue;
            _scratch[0] = (byte)num;
            _scratch[1] = (byte)((uint)num >> 8);
            Write(_scratch, 2U);
        }

        public void WriteInt32(int value)
        {
            _scratch[0] = (byte)value;
            _scratch[1] = (byte)(value >> 8);
            _scratch[2] = (byte)(value >> 16);
            _scratch[3] = (byte)(value >> 24);
            Write(_scratch, 4U);
        }

        public void WriteUInt32(uint value)
        {
            _scratch[0] = (byte)value;
            _scratch[1] = (byte)(value >> 8);
            _scratch[2] = (byte)(value >> 16);
            _scratch[3] = (byte)(value >> 24);
            Write(_scratch, 4U);
        }

        public void WriteInt64(long value) => WriteUInt64((ulong)value);

        public void WriteUInt64(ulong value)
        {
            _scratch[0] = (byte)value;
            _scratch[1] = (byte)(value >> 8);
            _scratch[2] = (byte)(value >> 16);
            _scratch[3] = (byte)(value >> 24);
            _scratch[4] = (byte)(value >> 32);
            _scratch[5] = (byte)(value >> 40);
            _scratch[6] = (byte)(value >> 48);
            _scratch[7] = (byte)(value >> 56);
            Write(_scratch, 8U);
        }

        public unsafe void WriteSingle(float value)
        {
            uint num = *(uint*)&value;
            _scratch[0] = (byte)num;
            _scratch[1] = (byte)(num >> 8);
            _scratch[2] = (byte)(num >> 16);
            _scratch[3] = (byte)(num >> 24);
            Write(_scratch, 4U);
        }

        public unsafe void WriteDouble(double value) => WriteInt64(*(long*)&value);

        public void WriteString(string value)
        {
            if (value == null)
            {
                WriteUInt16(ushort.MaxValue);
            }
            else
            {
                if (value.Length >= short.MaxValue)
                    throw new ArgumentException("String too long");
                
                bool useUtf16 = false;
                foreach (char ch in value)
                {
                    if (ch > 'ÿ')
                    {
                        useUtf16 = true;
                        break;
                    }
                }

                uint preamble = (uint)value.Length;
                if (!useUtf16)
                    preamble |= 1 << 15;
                
                WriteUInt16((ushort)preamble);
                
                foreach (char ch in value)
                {
                    if (useUtf16)
                        WriteChar(ch);
                    else
                        WriteByte((byte)ch);
                }
            }
        }

        public void Write(ByteCodeReader value) => Write(value, 0U);

        public unsafe void Write(ByteCodeReader value, uint offset)
        {
            IntPtr intPtr = value.ToIntPtr(out uint size);
            if (offset > size)
                throw new ArgumentOutOfRangeException(nameof(offset));
            Write(new IntPtr(intPtr.ToInt64() + offset), (uint)(size - offset));
        }

        public unsafe void Write(byte[] buffer, uint count)
        {
            fixed (byte* pbData = buffer)
                Write(pbData, count);
        }

        public unsafe void Write(IntPtr buffer, uint count) => Write((byte*)buffer.ToPointer(), count);

        private unsafe void Write(byte* pbData, uint cbData)
        {
            while (cbData > 0U)
            {
                if (_cbFreeInBlock == 0U)
                {
                    _currentBlock = new byte[4096];
                    _blockList.Add(_currentBlock);
                    _cbFreeInBlock = 4096U;
                }
                uint num1 = cbData <= _cbFreeInBlock ? cbData : _cbFreeInBlock;
                uint num2 = 4096U - _cbFreeInBlock;
                Marshal.Copy(new IntPtr(pbData), _currentBlock, (int)num2, (int)num1);
                pbData += (int)num1;
                cbData -= num1;
                _cbFreeInBlock -= num1;
                _totalSize += num1;
            }
        }

        public void Overwrite(uint offset, uint value)
        {
            if (offset + 4U > _totalSize)
                throw new ArgumentException("Invalid offset");
            OverwriteByte(offset, (byte)value);
            OverwriteByte(offset + 1U, (byte)(value >> 8));
            OverwriteByte(offset + 2U, (byte)(value >> 16));
            OverwriteByte(offset + 3U, (byte)(value >> 24));
        }

        private void OverwriteByte(uint offset, byte value) => ((byte[])_blockList[(int)(offset / 4096U)])[(offset % 4096U)] = value;

        private unsafe byte* ComposeFinalBuffer(out uint totalSize)
        {
            byte* pointer = (byte*)NativeApi.MemAlloc(_totalSize, false).ToPointer();
            byte* numPtr = pointer;
            for (int index = 0; index < _blockList.Count - 1; ++index)
            {
                Marshal.Copy((byte[])_blockList[index], 0, new IntPtr(numPtr), 4096);
                numPtr += 4096;
            }
            uint num = 4096U - _cbFreeInBlock;
            if (_currentBlock != null && num != 0U)
                Marshal.Copy(_currentBlock, 0, new IntPtr(numPtr), (int)num);
            totalSize = _totalSize;
            _blockList.Clear();
            _currentBlock = null;
            _cbFreeInBlock = 0U;
            _totalSize = 0U;
            return pointer;
        }

        public unsafe ByteCodeReader CreateReader()
        {
            uint totalSize;
            return new ByteCodeReader(new IntPtr(ComposeFinalBuffer(out totalSize)), totalSize, true);
        }
    }
}
