private IPacket[] ProcessReceive(SocketAsyncEventArgs args)
        {
            var packetList = new List<IPacket>();
            var packetToken = args.UserToken as PacketToken;
            var bytesToProcess = args.BytesTransferred;

            if (bytesToProcess == 0)
            {
                //TODO: Fire event
                Disconnected = true;
                return null;
            }

            ReadPacket:

            // read header
            if (packetToken.HeaderReceiveOffset != PacketBuffer.HeaderSize) // we do not have the header
            {
                if (PacketBuffer.HeaderSize > bytesToProcess) // we got less that 7 bytes, some parts of the header
                {
                    Console.WriteLine("[Net:ID {0}] Not enough bytes to read header.", packetToken.TokenID);

                    Buffer.BlockCopy(args.Buffer, packetToken.ReceiveOffset, packetToken.Header, packetToken.HeaderReceiveOffset, bytesToProcess);
                    packetToken.HeaderReceiveOffset += bytesToProcess;
                    packetToken.ReceiveOffset = 0;
                    StartReceive(args);
                    return packetList.ToArray();
                }
                else // if we got more than enough data for the header
                {
                    Buffer.BlockCopy(args.Buffer, packetToken.ReceiveOffset, packetToken.Header, packetToken.HeaderReceiveOffset, PacketBuffer.HeaderSize);
                    packetToken.HeaderReceiveOffset += PacketBuffer.HeaderSize;
                    packetToken.ReceiveOffset += PacketBuffer.HeaderSize; // probs here?
                    bytesToProcess -= PacketBuffer.HeaderSize;
                    ReadHeader(packetToken);
                }
            }

            // read body
            if (packetToken.BodyReceiveOffset != packetToken.Length)
            {
                if (packetToken.Length - packetToken.BodyReceiveOffset > bytesToProcess) // if we dont have enough to read body
                {
                    Console.WriteLine("[Net:ID {0}] Not enough bytes to read body.", packetToken.TokenID);

                    Buffer.BlockCopy(args.Buffer, packetToken.ReceiveOffset, packetToken.Body, packetToken.BodyReceiveOffset, bytesToProcess);
                    packetToken.BodyReceiveOffset += bytesToProcess;
                    packetToken.ReceiveOffset = 0;
                    StartReceive(args);
                    return packetList.ToArray();
                }
                else // if we got more than enough data for the body
                {
                    Buffer.BlockCopy(args.Buffer, packetToken.ReceiveOffset, packetToken.Body, packetToken.BodyReceiveOffset, packetToken.Length - packetToken.BodyReceiveOffset);
                    bytesToProcess -= packetToken.Length - packetToken.BodyReceiveOffset;
                    packetToken.ReceiveOffset += packetToken.Length - packetToken.BodyReceiveOffset;
                    packetToken.BodyReceiveOffset += packetToken.Length;
                }
            }

            var packet = CreatePacketInstance(packetToken.ID);
            var packetDeData = (byte[])packetToken.Body.Clone();
            CoCCrypto.Decrypt(packetDeData);

            if (packet is UnknownPacket)
            {
                packet = new UnknownPacket()
                {
                    ID = packetToken.ID,
                    Length = packetToken.Length,
                    Version = packetToken.Version,
                    EncryptedData = packetToken.Body,
                    DecryptedData = packetDeData
                };
            }

            using (var reader = new PacketReader(new MemoryStream(packetDeData)))
            {
                try
                {
                    if (!(packet is UnknownPacket))
                        packet.ReadPacket(reader);
                }
                catch (Exception ex)
                {
                    if (PacketReceivedFailed != null)
                        PacketReceivedFailed(args, ex);
                    packetToken.Reset();
                    goto ReadPacket;
                }
            }

            if (packet is UpdateKeyPacket)
                UpdateCiphers(Seed, ((UpdateKeyPacket)packet).Key);

            packetList.Add(packet);
            packetToken.Reset();
            if (bytesToProcess != 0)
                goto ReadPacket;

            packetToken.ReceiveOffset = 0;
            ReceiveEventPool.Push(args);
            StartReceive(ReceiveEventPool.Pop());
            return packetList.ToArray();
        }
        public IPacket ReadPacket(out byte[] rawPacket, out byte[] decryptedPacket)
        {
            /* Receive data from the socket, saves it a buffer,
             * then reads packet from the buffer. 
             */

            var timeout = DateTime.Now.AddMilliseconds(500); // 500ms
            while (DataAvailable && DateTime.Now < timeout)
            {
                CoCStream.ReadToBuffer(); // reads data saves it a buffer

                //var packetBuffer = new PacketBuffer(CoCStream.ReadBuffer.ToArray());
                var enPacketReader = new PacketReader(CoCStream.ReadBuffer);

                // read header
                var packetID = enPacketReader.ReadUInt16();
                var packetLength = enPacketReader.ReadInt24();
                var packetVersion = enPacketReader.ReadUInt16();

                // read body
                if (packetLength > enPacketReader.BaseStream.Length) // check if data is enough data is avaliable in the buffer
                    continue;

                var encryptedData = GetPacketBody(packetLength);
                var decryptedData = (byte[])encryptedData.Clone(); // cloning just cause we want the encrypted data

                CoCCrypto.Decrypt(decryptedData);

                var dePacketReader = new PacketReader(new MemoryStream(decryptedData));

                var packet = CreatePacketInstance(packetID);
                if (packet is UnknownPacket)
                {
                    packet = new UnknownPacket
                    {
                        ID = packetID,
                        Length = packetLength,
                        Version = packetVersion
                    };
                    ((UnknownPacket)packet).EncryptedData = encryptedData;
                }

                decryptedPacket = decryptedData;
                rawPacket = ExtractRawPacket(packetLength);
                //CoCStream.ReadBuffer = new MemoryStream(4096);
                //CoCStream.Write(packetBuffer.Buffer, 0, packetBuffer.Buffer.Length);
                try
                {
                    packet.ReadPacket(dePacketReader);
                }
                catch (Exception ex)
                {
                    if (ExceptionLog != null)
                        ExceptionLog.LogData(ex);
                }
                return packet;
            }
            decryptedPacket = null;
            rawPacket = null;
            return null;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        public IPacket[] ReadPackets(SocketAsyncEventArgs args)
        {
            var list = new List<IPacket>();
            var numBytesToProcess = args.BytesTransferred;
            if (numBytesToProcess == 0)
                return null;

            while (true)
            {
                if (numBytesToProcess == 0)
                    break;

                var packet = (IPacket)null;
                var packetBuffer = args.UserToken as PacketBuffer;
                try
                {
                    using (var enPacketReader = new PacketReader(new MemoryStream(packetBuffer.Buffer)))
                    {
                        if (PacketBuffer.HeaderSize > numBytesToProcess) // check if there is a header
                            break;

                        // read header
                        var packetID = enPacketReader.ReadUInt16();
                        var packetLength = enPacketReader.ReadInt24();
                        var packetVersion = enPacketReader.ReadUInt16(); // the unknown

                        // read body
                        if (packetLength > numBytesToProcess) // check if data is enough data is avaliable in the buffer
                            break;

                        var encryptedData = packetBuffer.ExtractPacket(PacketExtractionFlags.Body |
                                                                       PacketExtractionFlags.Remove,
                                                                       packetLength);
                        var decryptedData = (byte[])encryptedData.Clone(); // cloning just cause we want the encrypted data
                        CoCCrypto.Decrypt(decryptedData);
                        numBytesToProcess -= packetLength + PacketBuffer.HeaderSize;

                        using (var dePacketReader = new PacketReader(new MemoryStream(decryptedData)))
                        {
                            packet = CreatePacketInstance(packetID);
                            if (packet is UnknownPacket)
                            {
                                packet = new UnknownPacket
                                {
                                    ID = packetID,
                                    Length = packetLength,
                                    Version = packetVersion,
                                    EncryptedData = encryptedData,
                                    DecryptedData = decryptedData
                                };
                            }
                            packet.ReadPacket(dePacketReader);
                        }
                    }
                    if (packet is UpdateKeyPacket)
                        UpdateCiphers(Seed, ((UpdateKeyPacket)packet).Key); // handle update key packet
                    list.Add(packet);
                }
                catch (Exception ex)
                {
                    if (PacketReceivedFailed != null)
                        PacketReceivedFailed(args, ex);
                }
            }
            return list.ToArray();
        }