/// <summary>
 /// Updates the CC Packet State Buffer and Renders it.
 /// </summary>
 /// <param name="channel">The channel.</param>
 /// <param name="clockPosition">The clock position.</param>
 public void Render(CaptionsChannel channel, TimeSpan clockPosition)
 {
     if (Buffer.UpdateState(channel, clockPosition))
     {
         PaintBuffer();
     }
 }
        /// <summary>
        /// Updates the state using the packets that were demuxed into the specified channel.
        /// This can be called outside the GUI thread.
        /// </summary>
        /// <param name="channel">The channel.</param>
        /// <param name="clockPosition">The clock position.</param>
        /// <returns>A boolean to determine if the display needs repainting.</returns>
        public bool UpdateState(CaptionsChannel channel, TimeSpan clockPosition)
        {
            lock (SyncLock)
            {
                var needsRepaint = false;

                // Reset the buffer state if the channels don't match or if we have a timeout
                if (channel != Channel || DateTime.UtcNow.Subtract(LastReceiveTime).TotalSeconds > TimeoutSeconds)
                {
                    Reset();
                    Channel      = channel;
                    needsRepaint = true;
                }

                if (channel == CaptionsChannel.CCP)
                {
                    return(needsRepaint);
                }

                // Dequeue packets for all channels but only process the current channel packets
                List <ClosedCaptionPacket> packets = null;
                for (var c = 1; c <= 4; c++)
                {
                    var currentChannel  = (CaptionsChannel)c;
                    var dequeuedPackets = DequeuePackets(ChannelPacketBuffer[currentChannel], clockPosition.Ticks);
                    if (currentChannel == Channel)
                    {
                        packets = dequeuedPackets;
                    }
                }

                // Check if we have at least 1 dequeued packet
                if (packets == null || packets.Count <= 0)
                {
                    return(needsRepaint);
                }

                // Update the last received time
                LastReceiveTime = DateTime.UtcNow;

                // Start processing the dequeued packets for the given channel
                foreach (var packet in packets)
                {
                    // Skip duplicated control codes
                    if (CurrentPacket != null && CurrentPacket.IsRepeatedControlCode(packet))
                    {
                        CurrentPacket = packet;
                        continue;
                    }

                    // Update the current packet (we need this to detect duplicated control codes)
                    CurrentPacket = packet;

                    // Now, go ahead and process the packet updating the state
                    switch (packet.PacketType)
                    {
                    case CaptionsPacketType.Color:
                    {
                        if (StateMode == ParserStateMode.Buffered || StateMode == ParserStateMode.Scrolling)
                        {
                            if (packet.Color == CaptionsColor.ForegroundBlackUnderline)
                            {
                                IsUnderlined = true;
                            }

                            if (packet.Color == CaptionsColor.WhiteItalics)
                            {
                                IsItalics = true;
                            }
                        }

                        break;
                    }

                    case CaptionsPacketType.MidRow:
                    {
                        if (StateMode == ParserStateMode.Buffered || StateMode == ParserStateMode.Scrolling)
                        {
                            IsItalics    = packet.IsItalics;
                            IsUnderlined = packet.IsUnderlined;
                        }

                        break;
                    }

                    case CaptionsPacketType.Command:
                    {
                        if (ProcessCommandPacket(packet))
                        {
                            needsRepaint = true;
                        }

                        break;
                    }

                    case CaptionsPacketType.Preamble:
                    {
                        ProcessPreamblePacket(packet);
                        break;
                    }

                    case CaptionsPacketType.Tabs:
                    {
                        if (StateMode == ParserStateMode.Scrolling || StateMode == ParserStateMode.Buffered)
                        {
                            CursorColumnIndex += packet.Tabs;
                        }

                        break;
                    }

                    case CaptionsPacketType.Text:
                    {
                        if (ProcessTextPacket(packet))
                        {
                            needsRepaint = true;
                        }

                        break;
                    }

                    case CaptionsPacketType.XdsClass:
                    {
                        // Change state back and forth
                        StateMode = packet.XdsClass == CaptionsXdsClass.EndAll ?
                                    ParserStateMode.None : ParserStateMode.XDS;
                        break;
                    }

                    case CaptionsPacketType.PrivateCharset:
                    case CaptionsPacketType.Unrecognized:
                    case CaptionsPacketType.NullPad:
                    default:
                    {
                        break;
                    }
                    }
                }

                return(needsRepaint);
            }
        }