context: //
  // Paginator.cs:
  //
  // Author:
  //   Brian Nickel (brian.nickel@gmail.com)
  //
  // Original Source:
  //   oggpage.cpp from TagLib
  //
  // Copyright (C) 2006-2007 Brian Nickel
  // Copyright (C) 2003 Scott Wheeler (Original Implementation)
  //
  // This library is free software; you can redistribute it and/or modify
  // it  under the terms of the GNU Lesser General Public License version
  // 2.1 as published by the Free Software Foundation.
  //
  // This library is distributed in the hope that it will be useful, but
  // WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  // Lesser General Public License for more details.
  //
  // You should have received a copy of the GNU Lesser General Public
  // License along with this library; if not, write to the Free Software
  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
  // USA
  //
  
  using System;
  using System.Collections.Generic;
  
  namespace TagLib.Ogg
  {
  	/// <summary>
  	///    This class accepts a sequence of pages for a single Ogg stream,
  	///    accepts changes, and produces a new sequence of pages to write to
  	///    disk.
  	/// </summary>
  	public class Paginator
  	{
  #region Private Fields
  		
  		/// <summary>
  		///    Contains the packets to paginate.
  		/// </summary>
  		private ByteVectorCollection packets =
  			new ByteVectorCollection ();
  		
  		/// <summary>
  		///    Contains the first page header.
  		/// </summary>
  		private PageHeader? first_page_header = null;
  		
  		/// <summary>
  		///    Contains the codec to use.
  		/// </summary>
  		private Codec codec;
  		
  		/// <summary>
  		///    contains the number of pages read.
  		/// </summary>
  		private int pages_read = 0;
  #endregion
  		
  		
  		
  #region Constructors
  		
  		/// <summary>
  		///    Constructs and initializes a new instance of <see
  		///    cref="Paginator" /> for a given <see cref="Codec" />
  		///    object.
  		/// </summary>
  		/// <param name="codec">
  		///    A <see cref="Codec"/> object to use when processing
  		///    packets.
  		/// </param>
  		public Paginator (Codec codec)
  		{
  			this.codec = codec;
  		}
  		
  #endregion
  		
  		
  		
  #region Public Methods
  		
  		/// <summary>
  		///    Adds the next page to the current instance.
  		/// </summary>
  		/// <param name="page">
  		///    The next <see cref="Page" /> object found in the stream.
  		/// </param>
  		public void AddPage (Page page)
  		{
  			pages_read ++;
  			
  			if (first_page_header == null)
  				first_page_header = page.Header;
  			
  			if (page.Packets.Length == 0)
  				return;
  			
  			ByteVector[] page_packets = page.Packets;
  			
  			for (int i = 0; i < page_packets.Length; i ++) {
  				if ((page.Header.Flags & PageFlags
  					.FirstPacketContinued) != 0 && i == 0 &&
  					packets.Count > 0)
  					packets [packets.Count - 1].Add (page_packets [0]);
  				else
  					packets.Add (page_packets [i]);
  			}
  		}
  		
  		/// <summary>
  		///    Stores a Xiph comment in the codec-specific comment
  		///    packet.
  		/// </summary>
  		/// <param name="comment">
  		///    A <see cref="XiphComment" /> object to store in the
  		///    comment packet.
  		/// </param>
  		public void SetComment (XiphComment comment)
  		{
  			codec.SetCommentPacket (packets, comment);
  		}
  		
  		/// <summary>
  		///    Repaginates the pages passed into the current instance to
  		///    handle changes made to the Xiph comment.
  		/// </summary>
  		/// <returns>
  		///    A <see cref="Page[]" /> containing the new page
  		///    collection.
  		/// </returns>
  		[Obsolete("Use Paginator.Paginate(out int)")]
  		public Page [] Paginate ()
  		{
  			int dummy;
  			return Paginate (out dummy);
  		}
  		
  		/// <summary>
  		///    Repaginates the pages passed into the current instance to
  		///    handle changes made to the Xiph comment.
  		/// </summary>
  		/// <param name="change">
  		///    A <see cref="int" /> value reference containing the
  		///    the difference between the number of pages returned and
  		///    the number of pages that were added to the class.
  		/// </param>
  		/// <returns>
  		///    A <see cref="Page[]" /> containing the new page
  		///    collection.
  		/// </returns>
  		public Page [] Paginate (out int change)
  		{
  			// Ogg Pagination: Welcome to sucksville!
  			// If you don't understand this, you're not alone.
  			// It is confusing as Hell.
  			
  			// TODO: Document this method, in the mean time, there
  			// is always http://xiph.org/ogg/doc/framing.html
  			
  			if (pages_read == 0) {
  				change = 0;
  				return new Page [0];
  			}
  			
  			int count = pages_read;
  			ByteVectorCollection packets = new ByteVectorCollection (
  				this.packets);
  			PageHeader first_header = (PageHeader) first_page_header;
  			List<Page> pages = new List<Page> ();
  			uint index = 0;
  			bool bos = first_header.PageSequenceNumber == 0;
  			
  			if (bos) {
  				pages.Add (new Page (new ByteVectorCollection (packets [0]), first_header));
  				index ++;
  				packets.RemoveAt (0);
  				count --;
  			}
  			
  			int lacing_per_page = 0xfc;
  			if (count > 0) {
  				int total_lacing_bytes = 0;
  				
  				for (int i = 0; i < packets.Count; i ++)
  					total_lacing_bytes += GetLacingValueLength (
  						packets, i);
  				
  				lacing_per_page = Math.Min (total_lacing_bytes / count + 1, lacing_per_page);
  			}
  			
  			int lacing_bytes_used = 0;
  			ByteVectorCollection page_packets = new ByteVectorCollection ();
  			bool first_packet_continued = false;
  			
  			while (packets.Count > 0) {
  				int packet_bytes = GetLacingValueLength (packets, 0);
  				int remaining = lacing_per_page - lacing_bytes_used;
  				bool whole_packet = packet_bytes <= remaining;
  				if (whole_packet) {
  					page_packets.Add (packets [0]);
  					lacing_bytes_used += packet_bytes;
  					packets.RemoveAt (0);
  				} else {
  					page_packets.Add (packets [0].Mid (0, remaining * 0xff));
  					packets [0] = packets [0].Mid (remaining * 0xff);
  					lacing_bytes_used += remaining;
  				}
  				
  				if (lacing_bytes_used == lacing_per_page) {
  					pages.Add (new Page (page_packets,
  						new PageHeader (first_header,
  							index, first_packet_continued ?
  							PageFlags.FirstPacketContinued :
  							PageFlags.None)));
  					page_packets = new ByteVectorCollection ();
  					lacing_bytes_used = 0;
  					index ++;
  					count --;
  					first_packet_continued = !whole_packet;
  				}
  			}
  			
  			if (page_packets.Count > 0) {
  				pages.Add (new Page (page_packets,
  					new PageHeader (
  						first_header.StreamSerialNumber,
  						index, first_packet_continued ?
  						PageFlags.FirstPacketContinued :
  						PageFlags.None)));
  				index ++;
  				count --;
  			}
  			change = -count;
  			return pages.ToArray ();
  		}
  		
  #endregion
  		
  		
  		
  #region Private Methods
  		
  		/// <summary>
  		///    Gets the number of lacing value bytes that would be
  		///    required for a given packet.
  		/// </summary>
  		/// <param name="packets">
  		///    A <see cref="ByteVectorCollection" /> object containing
  		///    the packet.
  		/// </param>
  		/// <param name="index">
  		///    A <see cref="int" /> value containing the index of the
  		///    packet to compute.
  		/// </param>
  		/// <returns>
  		///    A <see cref="int" /> value containing the number of bytes
  		///    needed to store the length.
  		/// </returns>
  		private static int GetLacingValueLength (ByteVectorCollection packets,
  		                                         int index)
  		{
  			int size = packets [index].Count;
  			return size / 0xff + ((index + 1 < packets.Count ||
  
gt: 				size % 0xff > 0) ? 1 : 0);