/// <summary>
      /// Attempts to read an entire frame. Only finishes when a whole frame is read or an exception is encountered
      /// </summary>
      /// <returns>The status of the read operation</returns>
      /// <param name="frame">The parsed frame (on success)</param>
      public async Task<Tuple<DataStatus, WebSocketFrame>> ReadFrameAsync()
      {
         WebSocketFrame frame = new WebSocketFrame();
         DataStatus readStatus = DataStatus.Complete;
         WebSocketHeader header = null;
         byte[] message = null;

         Func<DataStatus, Tuple<DataStatus,WebSocketFrame>> FramePackage = x => Tuple.Create(x, frame);
         Func<bool> NoFrameInBuffer = () => (header == null || messageBufferSize < header.FrameSize);

         //Continue reading until we get a full frame. If the result is ever NOT complete 
         //(as in no read was performed), we should get the hell outta here
         do
         {
            //Note: we START with parsing the header just in case we have a leftover frame in the buffer.
            //We don't want to read more messages until all frames in the buffer have been parsed.

            //Console.WriteLine("Trying to read whole frame. Buffer: " + messageBufferSize);

            //Oh good, there's at least enough to know the header size.
            if(messageBufferSize >= 2)
            {
               message = messageBuffer.Take(messageBufferSize).ToArray();

               //OK we KNOW we read a whole header at this point.
               if(messageBufferSize >= WebSocketHeader.FullHeaderSize(message))
               {
                  //If we can't parse the header at this point, we have some serious issues.
                  if (!WebSocketHeader.TryParse(message, out header))
                     return FramePackage(DataStatus.InternalError);

                  //Too much data
                  if (header.FrameSize > MaxReceiveSize)
                     return FramePackage(DataStatus.OversizeError);
               }
            }
               
            //Only read if there's no full frame in the buffer.
            if(NoFrameInBuffer())
               readStatus = await GenericReadAsync();

            //If there was an error (anything other than "completion" or waiting), return the error.
            if (readStatus != DataStatus.Complete)
               return FramePackage(readStatus);
            
         }while(NoFrameInBuffer());

         //Oh, we have the whole message. Uhh ok then, let's make sure the header fields are correct
         //before continuing. RSV needs to be 0 (may change later) and all client messages must be masked.
         if (!header.Masked || header.RSV != 0)
            return FramePackage(DataStatus.DataFormatError);

         //Oh is this... a binary frame? Dawg... don't gimme that crap.
         if (header.Opcode == HeaderOpcode.BinaryFrame)
            return FramePackage(DataStatus.UnsupportedError);

         //Initialize a frame with our newly parsed data
         frame = new WebSocketFrame(header, messageBuffer.Take(header.FrameSize).ToArray());

         //Remove the message data from the buffer
         MessageBufferPop(header.FrameSize);

         return FramePackage(DataStatus.Complete);
      }
      /// <summary>
      /// Process given frame. Returns true if the connection can continue
      /// </summary>
      /// <returns><c>true</c> if everything was fine, <c>false</c> otherwise.</returns>
      /// <param name="readFrame">Read frame.</param>
      public bool ProcessFrame(WebSocketFrame readFrame, out byte[] outputBytes, out string message)
      {
         outputBytes = null;
         message = "";

         //If it's a message frame or PART of a message frame, we should add the payload to the 
         //fragment buffer. The fragment buffer will be complete if this is a fin frame (see next statement)
         if (readFrame.Header.Opcode == HeaderOpcode.ContinueFrame || readFrame.Header.Opcode == HeaderOpcode.TextFrame)
         {
            if (readFrame.Header.Opcode == HeaderOpcode.ContinueFrame)
               Log("Received fragmented frame.", LogLevel.SuperDebug);
            
            Array.Copy(readFrame.PayloadData, 0, fragmentBuffer, fragmentBufferSize, readFrame.Header.PayloadSize);
            fragmentBufferSize += readFrame.Header.PayloadSize;
         }

         //Only convert fragment buffer into message if this is the final frame and it's a text frame
         if (readFrame.Header.Fin && (readFrame.Header.Opcode == HeaderOpcode.TextFrame || readFrame.Header.Opcode == HeaderOpcode.ContinueFrame))
         {
            message = System.Text.Encoding.UTF8.GetString(fragmentBuffer, 0, fragmentBufferSize);
            fragmentBufferSize = 0;

            Log("Received message: " + message, LogLevel.SuperDebug);
            //User.ReceivedMessage(message);
         }
         else if (readFrame.Header.Opcode == HeaderOpcode.PingFrame)
         {
            Log("Client ping. Sending pong", LogLevel.SuperDebug);
            outputBytes = WebSocketFrame.GetPongFrame().GetRawBytes();
         }
         //Oh they're disconnecting? OK then, we need to finish up. Do NOT send more data.
         else if (readFrame.Header.Opcode == HeaderOpcode.CloseConnectionFrame)
         {
            Log("Client is disconnecting: " + readFrame.CloseCode, LogLevel.Debug);
            readFrame.Header.Masked = false;
            outputBytes = readFrame.GetRawBytes();
            return false;
         }

         return true;
      }
      /// <summary>
      /// Attempts to read an entire frame. Returns the frame if one was successfully read.
      /// </summary>
      /// <returns>The status of the read operation</returns>
      /// <param name="frame">The parsed frame (on success)</param>
      public DataStatus TryReadFrame(out WebSocketFrame frame)
      {
         frame = new WebSocketFrame();

         //Pull a chunk of data (as much as we can) from the stream and store it in our internal buffer.
         DataStatus readStatus = GenericRead();

         //If there was an error (anything other than "completion" or waiting), return the error.
         if (readStatus != DataStatus.Complete)
            return readStatus;

         //We MAY have read more than one frame at a time! Wowie...
//         while (messageBufferSize > 0)
//         {
            //We need at least 2 bytes to complete the header.
            if (messageBufferSize < 2)
               return DataStatus.WaitingOnData;

            byte[] message = messageBuffer.Take(messageBufferSize).ToArray();

            //If the complete header hasn't been read yet, we're still waiting for it.
            if (messageBufferSize < WebSocketHeader.FullHeaderSize(message))
               return DataStatus.WaitingOnData;

            WebSocketHeader header = new WebSocketHeader();

            //If we can't parse the header at this point, we have some serious issues.
            if (!WebSocketHeader.TryParse(message, out header))
               return DataStatus.InternalError;
         
            //Too much data
            if (header.FrameSize > MaxReceiveSize)
            {
               return DataStatus.OversizeError;
            }
         
            //We have the whole header, but do we have the whole message? if not, we're still waiting on data.
            if (messageBufferSize < header.FrameSize)
               return DataStatus.WaitingOnData;

            //Oh, we have the whole message. Uhh ok then, let's make sure the header fields are correct
            //before continuing. RSV needs to be 0 (may change later) and all client messages must be masked.
            if (!header.Masked || header.RSV != 0)
               return DataStatus.DataFormatError;

            //Oh is this... a binary frame? Dawg... don't gimme that crap.
            if (header.Opcode == HeaderOpcode.BinaryFrame)
               return DataStatus.UnsupportedError;

            //Initialize a frame with our newly parsed data
            frame = new WebSocketFrame(header, messageBuffer.Take(header.FrameSize).ToArray());

            //Remove the message data from the buffer
            MessageBufferPop(header.FrameSize);
//            messageBuffer.TruncateBeginning(header.FrameSize, messageBufferSize);
//            messageBufferSize -= header.FrameSize;
         //}

         return DataStatus.Complete;
      }