/*

		PACKET
		======

		 Byte/     0       |       1       |       2       |       3       |
			/              |               |               |               |
			|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
			+---------------+---------------+---------------+---------------+
			0/ HEADER                                                        /
			/                                                               /
			/                                                               /
			/                                                               /
			+---------------+---------------+---------------+---------------+
			24/ COMMAND-SPECIFIC EXTRAS (as needed)                           /
			+/  (note length in the extras length header field)              /
			+---------------+---------------+---------------+---------------+
			m/ Key (as needed)                                               /
			+/  (note length in key length header field)                     /
			+---------------+---------------+---------------+---------------+
			n/ Value (as needed)                                             /
			+/  (note length is total body length header field, minus        /
			+/   sum of the extras and key length body fields)               /
			+---------------+---------------+---------------+---------------+
			Total 24 bytes
		*/
		bool IRequest.WriteTo(WriteBuffer buffer)
		{
			// 0. init header
			// 1. loop on header
			// 2. loop on Key, if any
			// 3. loop on Data, if any
			// 4. done
			switch (state)
			{
				case STATE_WRITE_HEADER:
					// this will either get a Span that can fit the header data or nothing
					// we will not write partial header
					var span = buffer.Want(Protocol.HeaderLength);
					if (span.IsEmpty) return true; // header does not fit into the request buffer

					WriteHeader(span);
					if (finalBody.IsEmpty) break; // no body, quit

					writeOffset = 0;
					if (finalBody.IsSingleSegment)
						singleSegmentBody = finalBody.First;
					else
						bodyCopier = new SequenceCopier(finalBody, buffer);

					state = STATE_WRITE_BODY;
					goto case STATE_WRITE_BODY;

				case STATE_WRITE_BODY:

					// body is only one span, write it until it's gone
					if (!singleSegmentBody.IsEmpty)
					{
						writeOffset += buffer.TryAppend(singleSegmentBody.Slice(writeOffset).Span);
						if (writeOffset < singleSegmentBody.Length) return true;

						Debug.Assert(writeOffset == singleSegmentBody.Length);
					}
					else
					{
						Debug.Assert(bodyCopier != null);
						// body consists of munltiple spans; the SequenceCopier will process it
						if (bodyCopier.Copy()) return true;
						bodyCopier = default;
					}

					break;

				default: throw new InvalidOperationException("undhandled state: " + state); // should not happen
			}

			state = STATE_DONE;

			//bodyBuilder.Dispose();
			finalBody = default;

			return false;
		}