private void ui_cbAllMenus_CheckedChanged(object sender, RoutedEventArgs e)
        {
            ExtGossipMenu[] arr;

            if ((bool)ui_cbAllMenus.IsChecked)
            {
                arr = m_menus.Select(pair => pair.Value).ToArray();
            }
            else
            {
                arr = new ExtGossipMenu[0];
            }

            SetProgressBarVisible(true);
            m_creatingMenuDataWorker.RunWorkerAsync(arr);
        }
        void CreateSqlForMenu(ExtGossipMenu menu, StringBuilder builder)
        {
            builder.Append("-- ").AppendLine(Strings.Gossip_SqlStructure.LocalizedFormat(menu.Menu.MenuId));

            foreach (var guid in menu.Objects)
            {
                var obj = m_objects.First(_ => _.Key == guid).Value;

                if (obj.ObjectType == WowGuidKind.Unit)
                {
                    if (obj.InitialMenus.Count == 1 && obj.InitialMenus.First() == menu.Menu.MenuId)
                    {
                        builder.AppendFormatLine(
                            "UPDATE creature_template SET gossip_menu_id = {0} WHERE entry = {1}; -- {2}",
                            menu.Menu.MenuId, obj.ObjectEntry, obj.ObjectName);
                    }
                    else
                    {
                        builder.Append("-- ").AppendLine(Strings.Gossip_SqlInitialMenus
                                                         .LocalizedFormat(obj.ObjectName, obj.InitialMenus.ToStringJoin(", ")));
                    }
                }
                else
                {
                    builder.Append("-- ").AppendLine(Strings.Gossip_SqlUnsupportedObject
                                                     .LocalizedFormat(obj.ObjectType, obj.ObjectName));
                }
            }

            CultureInfo culture = null;

            if (m_viewer.CurrentLog is IHasCultureInfo)
            {
                culture = ((IHasCultureInfo)m_viewer.CurrentLog).Culture;
            }

            var locale = WowLocales.enUS;

            if (culture != null && culture.IsWowCompatibleCulture())
            {
                locale = culture.GetWowLocale();
            }

            bool defaultLocale = locale == WowLocales.enUS;

            builder.AppendFormatLine("REPLACE INTO gossip_menu (entry, text_id) VALUES ({0}, {1});",
                                     menu.Menu.MenuId, menu.Menu.TextId);

            var length = menu.Menu.GossipOptions.Length;

            if (length > 0)
            {
                builder.AppendLine("REPLACE INTO gossip_menu_option (menu_id, id, option_icon, option_text, npc_option_npcflag, action_menu_id, action_poi_id, box_coded, box_money, box_text) VALUES");

                for (int i = 0; i < length; i++)
                {
                    var opt = menu.Menu.GossipOptions[i];

                    builder.AppendFormat(
                        "({0}, {1}, {2}, '{3}', {4}, {5}, {6}, {7}, {8}, '{9}')",
                        menu.Menu.MenuId, opt.OptionId, opt.Icon,
                        (defaultLocale ? "" : "[PH] ") + opt.Text.EscapeQuotes(), 0,
                        menu.GossipOptionMenus[i], 0, opt.IsPasswordRequired ? 1 : 0, opt.MoneyRequired,
                        (!defaultLocale && opt.BoxText != string.Empty ? "[PH] " : "") + opt.BoxText.EscapeQuotes());

                    if (i + 1 == length)
                    {
                        builder.AppendLine(";");
                    }
                    else
                    {
                        builder.AppendLine(",");
                    }
                }

                if (!defaultLocale)
                {
                    builder.AppendFormatLine(
                        "REPLACE INTO locales_gossip_menu_option " +
                        "(menu_id, id, option_text_loc{0}, box_text_loc{0}) VALUES",
                        (int)locale);

                    for (int i = 0; i < length; i++)
                    {
                        var opt = menu.Menu.GossipOptions[i];

                        builder.AppendFormat(
                            "({0}, {1}, '{2}', '{3}')",
                            menu.Menu.MenuId, opt.OptionId, opt.Text.EscapeQuotes(), opt.BoxText.EscapeQuotes());

                        if (i + 1 == length)
                        {
                            builder.AppendLine(";");
                        }
                        else
                        {
                            builder.AppendLine(",");
                        }
                    }
                }
            }
        }
        void m_parsingItemsWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var worker   = (BackgroundWorker)sender;
            var items    = (IList <ViewerItem>)e.Argument;
            var nitems   = items.Count;
            int progress = 0;
            var protocol = m_viewer.CurrentProtocol;

            var objects = new SortedDictionary <WowGuid, ObjectGossip>(new WowGuidKindEntryComparer());
            var menus   = new SortedDictionary <uint, ExtGossipMenu>();

            ExtGossipMenu currentMenu   = null;
            WowGuid       currentObject = WowGuid.Empty;
            Action        resetCurrent  = () =>
            {
                currentMenu   = null;
                currentObject = WowGuid.Empty;
            };

            var optSelectQueue = new Queue <GossipSelectOption>();

            for (int i = 0; i < nitems && !worker.CancellationPending; i++)
            {
                var item         = items[i];
                var packet       = item.Packet;
                var opcodePacket = packet as IPacketWithOpcode;
                if (opcodePacket == null)
                {
                    continue;
                }

                var opcode = (WowOpcodes)opcodePacket.Opcode;

                switch (opcode)
                {
#warning Gossip Parser not implemented
                //case WowOpcodes.CMSG_GOSSIP_HELLO:
                //case WowOpcodes.CMSG_GAMEOBJ_USE:
                //case WowOpcodes.CMSG_LIST_INVENTORY:
                //case WowOpcodes.CMSG_TRAINER_LIST:
                //{
                //    using (var reader = new StreamHandler(packet.Data))
                //    {
                //        optSelectQueue.Enqueue(new GossipSelectOption()
                //        {
                //            Object = reader.ReadGuid(),
                //            MenuId = uint.MaxValue,
                //            OptionId = uint.MaxValue
                //        });
                //    }
                //    break;
                //}
                //case WowOpcodes.CMSG_GOSSIP_SELECT_OPTION:
                //{
                //    if (currentMenu != null)
                //    {
                //        using (var reader = new StreamHandler(packet.Data))
                //        {
                //            var obj = reader.ReadGuid();
                //            var menuId = reader.ReadUInt32();
                //            var optionId = reader.ReadUInt32();
                //            if (currentMenu.Menu.MenuId == menuId)
                //            {
                //                optSelectQueue.Enqueue(new GossipSelectOption()
                //                {
                //                    Object = obj,
                //                    MenuId = menuId,
                //                    OptionId = optionId
                //                });
                //            }
                //            else
                //                Console.WriteLine("Error: Gossip Menu: currentMenuId != selectedMenuId ({0} != {1})",
                //                    currentMenu.Menu.MenuId, menuId);
                //        }
                //    }
                //    else
                //        Console.WriteLine("Error: Gossip Parser: Unexpected CMSG_GOSSIP_SELECT_OPTION (inner)");
                //    break;
                //}
                //case WowOpcodes.SMSG_DESTROY_OBJECT:
                //{
                //    var guid = new WowGuid(BitConverter.ToUInt64(packet.Data, 0));
                //    if (currentObject == guid)
                //        resetCurrent();
                //    break;
                //}
                //case WowOpcodes.SMSG_UPDATE_OBJECT:
                //{
                //    using (var reader = new StreamHandler(packet.Data))
                //    {
                //        var updateData = new UpdateData(reader, true);
                //        foreach (var guid in updateData.DestroyedObjects)
                //        {
                //            if (currentObject == guid)
                //                resetCurrent();
                //        }
                //    }
                //    break;
                //}
                //case WowOpcodes.CMSG_QUESTGIVER_ACCEPT_QUEST:
                //{
                //    var guid = new WowGuid(BitConverter.ToUInt64(packet.Data, 0));
                //    if (optSelectQueue.Count > 0)
                //    {
                //        if (optSelectQueue.Peek().Object == guid)
                //            optSelectQueue.Dequeue();
                //        else
                //            Console.WriteLine("Error: Gossip Parser: Unexpected {0} (packet #{1})", opcode, i);
                //    }
                //    break;
                //}
                //case WowOpcodes.SMSG_TRAINER_LIST:
                //case WowOpcodes.SMSG_VENDOR_INVENTORY:
                //case WowOpcodes.SMSG_INVALID_PROMOTION_CODE:
                //{
                //    if (optSelectQueue.Count > 0)
                //        optSelectQueue.Dequeue();
                //    else
                //        Console.WriteLine("Error: Gossip Parser: Unexpected {0} (packet #{1})", opcode, i);
                //    break;
                //}
                //case WowOpcodes.SMSG_GOSSIP_COMPLETE:
                //{
                //    while (optSelectQueue.Count > 0 &&
                //        optSelectQueue.Peek().Object == currentObject &&
                //        optSelectQueue.Peek().MenuId == currentMenu.Menu.MenuId)
                //        optSelectQueue.Dequeue();
                //    resetCurrent();
                //    break;
                //}
                //case WowOpcodes.CMSG_LOADING_SCREEN_NOTIFY:
                //case WowOpcodes.SMSG_NEW_WORLD:
                case WowOpcodes.SMSG_LOGIN_VERIFY_WORLD:
                {
                    resetCurrent();
                    optSelectQueue.Clear();
                    break;
                }

                default:
                {
                    var parser = item.Parser;
                    if (parser == null)
                    {
                        protocol.CreateParser(item);
                        parser = item.Parser;
                    }

                    if (parser is GossipMessageParser)
                    {
                        var gossip = (GossipMessageParser)parser;
                        if (!gossip.IsParsed)
                        {
                            gossip.Parse();
                        }

                        var           menu = gossip.Menu;
                        ExtGossipMenu extMenu;
                        if (!menus.TryGetValue(menu.MenuId, out extMenu))
                        {
                            menus[menu.MenuId] = extMenu = new ExtGossipMenu()
                            {
                                Menu = menu,
                                GossipOptionMenus = new uint[menu.GossipOptions.Length]
                            };
                        }
                        else
                        {
                            var existingMenu = extMenu.Menu;
                            if (!existingMenu.Equals(menu))
                            {
                                Console.WriteLine("Error: Gossip Parser: New menu with same id {0}",
                                                  menu.MenuId);
                                break;
                            }
                        }

                        var guid = gossip.Object;

                        extMenu.Objects.Add(guid);

                        ObjectGossip gossipObject;

                        if (!objects.TryGetValue(guid, out gossipObject))
                        {
                            objects[guid] = gossipObject = new ObjectGossip()
                            {
                                ObjectType  = guid.Kind,
                                ObjectEntry = guid.Entry
                            };
                        }

                        GossipSelectOption opt = null;

                        while (optSelectQueue.Count > 0)
                        {
                            var optSelect = optSelectQueue.Dequeue();

                            if (optSelect.Object != guid && optSelect.Object.IsGameObject)
                            {
                                continue;
                            }

                            opt = optSelect;
                            break;
                        }

                        if (opt == null)
                        {
                            Console.WriteLine("Error: Gossip Parser: Unexpected Gossip Message");
                            break;
                        }

                        if ((opt.MenuId == uint.MaxValue) != (currentMenu == null))
                        {
                            Console.WriteLine("Error: Gossip Parser: unexpected state of gossip parser, packet = {0}", i);
                        }

                        if (currentMenu == null)
                        {
                            // Just interacted with the object
                            gossipObject.InitialMenus.Add(menu.MenuId);
                            currentObject = guid;
                        }
                        else
                        {
                            if (guid != currentObject)
                            {
                                Console.WriteLine("Error: Gossip Parser: unexpected object");
                                break;
                            }

                            // Got a gossip message after an option was selected
                            int idx = currentMenu.Menu.GossipOptions.IndexOf(_ => _.OptionId == opt.OptionId);
                            if (idx != -1)
                            {
                                currentMenu.GossipOptionMenus[idx] = menu.MenuId;
                            }
                            else
                            {
                                Console.WriteLine("Error: Gossip Parser: Unable to find option {0} in menu {1}",
                                                  opt.OptionId, opt.MenuId);
                            }
                        }

                        if (gossipObject.Menus.IndexOf(extMenu) < 0)
                        {
                            gossipObject.Menus.Add(extMenu);
                        }
                        currentMenu = extMenu;
                    }
                    else if (parser is CreatureQueryResponseParser)
                    {
                        var query = (CreatureQueryResponseParser)parser;
                        if (!query.IsParsed)
                        {
                            query.Parse();
                        }

                        var id = query.Template.Entry;
                        if (id < 0)
                        {
                            continue;
                        }

                        m_creatureTemplates[(uint)id] = query.Template;
                    }
                    else if (parser is GameObjectQueryResponseParser)
                    {
                        var query = (GameObjectQueryResponseParser)parser;
                        if (!query.IsParsed)
                        {
                            query.Parse();
                        }

                        var id = query.Template.Entry;
                        if (id < 0)
                        {
                            continue;
                        }

                        m_goTemplates[(uint)id] = query.Template;
                    }
                    break;
                }
                }

                int newProgress = 100 * i / nitems;
                if (newProgress != progress)
                {
                    worker.ReportProgress(progress = newProgress);
                }
            }

            if (worker.CancellationPending)
            {
                m_objects = new KeyValuePair <WowGuid, ObjectGossip> [0];
                m_menus   = new KeyValuePair <uint, ExtGossipMenu> [0];
                return;
            }

            // Setup names.
            foreach (var pair in objects)
            {
                var obj = pair.Value;
                var s   = obj.ObjectType.ToString() + " #" + obj.ObjectEntry.ToString();

                if (obj.ObjectType == WowGuidKind.GameObject)
                {
                    GameObjectTemplate go;
                    if (m_goTemplates.TryGetValue(obj.ObjectEntry, out go))
                    {
                        s += " - " + go.Name[0];
                    }
                }
                else if (obj.ObjectType == WowGuidKind.Unit)
                {
                    CreatureTemplate npc;
                    if (m_creatureTemplates.TryGetValue(obj.ObjectEntry, out npc))
                    {
                        s += " - " + npc.Name[0] + (string.IsNullOrEmpty(npc.SubName) ? "" : " <" + npc.SubName + ">");
                    }
                }

                obj.ObjectName = s;
            }

            m_objects = objects.ToArray();
            m_menus   = menus.ToArray();
        }