context: using System;
  using System.Diagnostics;
  using System.IO;
  using System.Text;
  using System.Threading;
  using fNbt;
  using MiNET.Blocks;
  using MiNET.Utils;
  
  namespace MiNET.Net
  {
  	public abstract partial class Package
  	{
  		protected object _bufferSync = new object();
  		private bool _isEncoded = false;
  		private byte[] _encodedMessage;
  
  		public byte Id;
  
  		protected MemoryStream _buffer;
  		private BinaryWriter _writer;
  		private BinaryReader _reader;
  		private Stopwatch _timer = new Stopwatch();
  
  		public Package()
  		{
  			_buffer = new MemoryStream();
  			_reader = new BinaryReader(_buffer);
  			_writer = new BinaryWriter(_buffer);
  			Timer.Start();
  		}
  
  		public Stopwatch Timer
  		{
  			get { return _timer; }
  		}
  
  		public void Write(byte value)
  		{
  			_writer.Write(value);
  		}
  
  		public byte ReadByte()
  		{
  			return _reader.ReadByte();
  		}
  
  		public void Write(sbyte value)
  		{
  			_writer.Write(value);
  		}
  
  		public sbyte ReadSByte()
  		{
  			return _reader.ReadSByte();
  		}
  
  		public void Write(byte[] value)
  		{
  			if (value == null) return;
  
  			_writer.Write(value);
  		}
  
  		public byte[] ReadBytes(int count)
  		{
  			if (count == 0)
  			{
  				count = (int) (_reader.BaseStream.Length - _reader.BaseStream.Position);
  			}
  
  			var readBytes = _reader.ReadBytes(count);
  			if (readBytes.Length != count) throw new ArgumentOutOfRangeException();
  			return readBytes;
  		}
  
  		public void Write(short value)
  		{
  			_writer.Write(Endian.SwapInt16(value));
  		}
  
  		public short ReadShort()
  		{
  			if (_reader.BaseStream.Position == _reader.BaseStream.Length) return 0;
  
  			return Endian.SwapInt16(_reader.ReadInt16());
  		}
  
  		public void Write(ushort value)
  		{
  			_writer.Write(Endian.SwapUInt16(value));
  		}
  
  		public ushort ReadUShort()
  		{
  			return Endian.SwapUInt16(_reader.ReadUInt16());
  		}
  
  		public void Write(Int24 value)
  		{
  			_writer.Write(value.GetBytes());
  		}
  
  		public Int24 ReadLittle()
  		{
  			return new Int24(_reader.ReadBytes(3));
  		}
  
  		public void Write(int value)
  		{
  			_writer.Write(Endian.SwapInt32(value));
  		}
  
  		public int ReadInt()
  		{
  			return Endian.SwapInt32(_reader.ReadInt32());
  		}
  
  		public void Write(uint value)
  		{
  			_writer.Write(Endian.SwapUInt32(value));
  		}
  
  		public uint ReadUInt()
  		{
  			return Endian.SwapUInt32(_reader.ReadUInt32());
  		}
  
  		public void Write(long value)
  		{
  			_writer.Write(Endian.SwapInt64(value));
  		}
  
  		public long ReadLong()
  		{
  			return Endian.SwapInt64(_reader.ReadInt64());
  		}
  
  		public void Write(ulong value)
  		{
  			_writer.Write(Endian.SwapUInt64(value));
  		}
  
  		public ulong ReadULong()
  		{
  			return Endian.SwapUInt64(_reader.ReadUInt64());
  		}
  
  		public void Write(float value)
  		{
  			byte[] bytes = BitConverter.GetBytes(value);
  
  			_writer.Write(bytes[3]);
  			_writer.Write(bytes[2]);
  			_writer.Write(bytes[1]);
  			_writer.Write(bytes[0]);
  		}
  
  		public float ReadFloat()
  		{
  			byte[] buffer = _reader.ReadBytes(4);
  			return BitConverter.ToSingle(new[] {buffer[3], buffer[2], buffer[1], buffer[0]}, 0);
  		}
  
  		public void Write(string value)
  		{
  			byte[] bytes = Encoding.Default.GetBytes(value);
  
  			Write((short) bytes.Length);
  			Write(bytes);
  		}
  
  		public string ReadString()
  		{
  			if (_reader.BaseStream.Position == _reader.BaseStream.Length) return "";
  			short len = ReadShort();
  			return Encoding.Default.GetString(ReadBytes(len));
  		}
  
  		public void Write(Records records)
  		{
  			Write(records.Count);
  			foreach (BlockCoordinates coord in records)
  			{
  				Write((byte) coord.X);
  				Write((byte) coord.Y);
  				Write((byte) coord.Z);
  			}
  		}
  
  		public Records ReadRecords()
  		{
  			return new Records();
  		}
  
  		public void Write(BlockRecords records)
  		{
  			Write((int) records.Count);
  			foreach (Block block in records)
  			{
  				Write(block.Coordinates.X);
  				Write(block.Coordinates.Z);
  				Write((byte) block.Coordinates.Y);
  				Write((byte) block.Id);
  
gt: 				Write((byte) block.Metadata); //TODO: Shift in the block flags