public static bool UpdateBubble(BubbleGroup group, VisualBubble[] bubbles, int searchDepth = 100)
        {
            //we can't operate if a thread/worker is concurrently processing a new bubble
            lock (OperationLock)
            {
                var groupDatabaseLocation = GetLocation(@group);
                var bubbleTuples = bubbles.Select(x => new Tuple<string, VisualBubble>(x.ID, x)).ToList();

                VisualBubble lastBubble = null;
                long insertPosition = -1;

                using (var stream = File.Open(groupDatabaseLocation, FileMode.Open, FileAccess.ReadWrite))
                {
                    stream.Seek(stream.Length, SeekOrigin.Begin);

                    for (var i = 0; i < (searchDepth != -1 ? searchDepth : Int32.MaxValue); i++)
                    {
                        if (stream.Position == 0)
                        {
                            break;
                        }

                        byte[] headerBytes;
                        int headerBytesLength;
                        int endPosition;

                        BubbleGroupDatabasePrimitives.ReadBubbleHeader(stream, out headerBytes, out headerBytesLength);
                        BubbleGroupDatabasePrimitives.FindBubbleHeaderDelimiter(headerBytes, headerBytesLength, 0, out endPosition);
                        var bubbleDataLength = BubbleGroupDatabasePrimitives.JumpBubbleData(stream); //we need to seek over the data.

                        var guid = BubbleGroupDatabasePrimitives.ReadBubbleHeaderStruct(headerBytes, headerBytesLength,
                            endPosition + 1, out endPosition);

                        Tuple<string, VisualBubble> bubbleTuple;
                        if ((bubbleTuple = bubbleTuples.FirstOrDefault(x => x.Item1 == guid)) == null)
                        {
                            continue;
                        }

                        var b = bubbleTuple.Item2;

                        var bubbleSize = headerBytesLength + bubbleDataLength + 8;
                        var cutStart = stream.Position + bubbleSize;
                        var cutLength = stream.Length - cutStart;

                        insertPosition = stream.Position;

                        using (var ms = new MemoryStream())
                        {
                            Serializer.Serialize(ms, b);

                            var updatedBubbleData = ms.ToArray();
                            var updatedBubbleHeader = b.GetType().Name + ":" + b.ID + ":" + b.Time;
                            var updatedBubbleSize = updatedBubbleHeader.Length + updatedBubbleData.Length + 8;

                            var bubbleInjectDelta = bubbleSize - updatedBubbleSize;
                            //enough room
                            if (bubbleInjectDelta != 0)
                            {
                                var injectPosition = stream.Position;

                                stream.Position = cutStart; //higher 
                                var cut = new byte[cutLength];
                                stream.Read(cut, 0, (int)cutLength);//should always work as long as the count ain't crazy high

                                //var bw = new BinaryWriter(stream, Encoding.ASCII);
                                stream.Position = injectPosition;
                                BubbleGroupDatabasePrimitives.WriteBubbleData(stream, updatedBubbleData);
                                BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, updatedBubbleHeader);

                                stream.Write(cut, 0, cut.Length);
                                stream.SetLength(stream.Position);
                            }
                            //they're the same!
                            else if (bubbleInjectDelta == 0)
                            {
                                //var bw = new BinaryWriter(stream, Encoding.ASCII);
                                BubbleGroupDatabasePrimitives.WriteBubbleData(stream, updatedBubbleData);
                                BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, updatedBubbleHeader);
                            }
                        }
                            
                        if (cutLength == 0)
                        {
                            lastBubble = b;
                        }

                        bubbleTuples.Remove(bubbleTuple);
                        if (!bubbleTuples.Any())
                        {
                            break;
                        }
                    }

                    BubbleGroupIndex.UpdateLastBubbleOrAndLastModifiedIndex(group.ID, lastBubble, insertPosition);
                }

                if (!bubbleTuples.Any())
                {
                    return true;
                }

                return false;
            }
        }
        public static bool InsertBubblesByTime(BubbleGroup group, VisualBubble[] bubbles, int maxDepth = 1000, bool guidCheck = false, bool insertAtTop = false)
        {
            //we can't operate if a thread/worker is concurrently processing a new bubble
            lock (OperationLock)
            {
                var groupDatabaseLocation = GetLocation(@group);
                var bubbleTuples = bubbles.Select(x => new Tuple<long, VisualBubble>(x.Time, x)).ToList();

                VisualBubble lastBubble = null;
                long insertPosition = -1;

                using (var stream = File.Open(groupDatabaseLocation, FileMode.Open, FileAccess.ReadWrite))
                {
                    stream.Seek(stream.Length, SeekOrigin.Begin);

                    for (var i = 0; i < (maxDepth != -1 ? maxDepth : Int32.MaxValue); i++)
                    {
                        if (stream.Position == 0)
                        {
                            if (insertAtTop)
                            {
                                insertPosition = 0;

                                var cut = new byte[(int)stream.Length];
                                stream.Read(cut, 0, (int)stream.Length);
                                stream.Position = 0;

                                var bubblesToInsert = bubbleTuples.Select(x => x.Item2).ToList();
                                bubblesToInsert.TimSort((x, y) => x.Time.CompareTo(y.Time));
                                foreach (var bubbleToInsert in bubblesToInsert)
                                {
                                    using (var ms = new MemoryStream())
                                    {
                                        Serializer.Serialize(ms, bubbleToInsert);

                                        var bubbleToInsertData = ms.ToArray();
                                        var bubbleToInsertHeader = bubbleToInsert.GetType().Name + ":" + bubbleToInsert.ID
                                                                   + ":" + bubbleToInsert.Time;

                                        BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleToInsertData);
                                        BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleToInsertHeader);
                                    }
                                }

                                stream.Write(cut, 0, cut.Length);
                                stream.SetLength(stream.Position);

                                break;
                            }
                            else
                            {
                                break;
                            }
                        }

                        byte[] headerBytes;
                        int headerBytesLength;
                        int endPosition;

                        BubbleGroupDatabasePrimitives.ReadBubbleHeader(stream, out headerBytes, out headerBytesLength);
                        BubbleGroupDatabasePrimitives.FindBubbleHeaderDelimiter(headerBytes, headerBytesLength, 0, out endPosition);
                        var bubbleDataLength = BubbleGroupDatabasePrimitives.JumpBubbleData(stream); //we need to seek over the data.

                        var guid = BubbleGroupDatabasePrimitives.ReadBubbleHeaderStruct(headerBytes, headerBytesLength,
                            endPosition + 1, out endPosition);
                        var time = BubbleGroupDatabasePrimitives.AsciiBytesToString(headerBytes, endPosition + 1,
                            headerBytesLength);
                        long longTime;
                        Int64.TryParse(time, out longTime);

                        {
                            var bubblesToInsert = bubbleTuples.Where(x =>
                            {
                                if (guidCheck && guid == x.Item2.ID)
                                    return false;

                                if (longTime <= x.Item1)
                                    return true;

                                return false;

                            }).ToList();
                            if (!bubblesToInsert.Any())
                            {
                                continue;
                            }

                            var bubbleSize = headerBytesLength + bubbleDataLength + 8;
                            var insertLocation = stream.Position + bubbleSize;
                            stream.Seek(insertLocation, SeekOrigin.Begin);

                            var cutLength = stream.Length - insertLocation;
                            var cut = new byte[cutLength];
                            stream.Read(cut, 0, (int)cutLength); //should always work as long as the count ain't crazy high

                            stream.Seek(insertLocation, SeekOrigin.Begin);

                            insertPosition = stream.Position;

                            foreach (var bubbleToInsert in bubblesToInsert.Select(x => x.Item2))
                            {
                                using (var ms = new MemoryStream())
                                {
                                    Serializer.Serialize(ms, bubbleToInsert);

                                    var bubbleToInsertData = ms.ToArray();
                                    var bubbleToInsertHeader = bubbleToInsert.GetType().Name + ":" + bubbleToInsert.ID
                                                               + ":" + bubbleToInsert.Time;

                                    BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleToInsertData);
                                    BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleToInsertHeader);
                                }
                            }

                            stream.Write(cut, 0, cut.Length);
                            stream.SetLength(stream.Position);

                            // adding to end
                            if (cut.Length == 0)
                            {
                                lastBubble = bubblesToInsert.Last().Item2;
                            }

                            foreach (var bubbleToInsert in bubblesToInsert)
                            {
                                bubbleTuples.Remove(bubbleToInsert);
                            }

                            if (!bubbleTuples.Any())
                            {
                                break;
                            }
                        }
                    }

                    BubbleGroupIndex.UpdateLastBubbleOrAndLastModifiedIndex(group.ID, lastBubble, insertPosition);
                }

                if (!bubbleTuples.Any())
                {
                    return true;
                }

                return false;
            }
        }