public static WhoInfo Parse(IrcMessageData data)
        {
            var whoInfo = new WhoInfo
            {
                // :fu-berlin.de 352 meebey_ * ~meebey e176002059.adsl.alicedsl.de fu-berlin.de meebey_ H :0 Mirco Bauer..
                Channel = data.RawMessageArray[3],
                Ident   = data.RawMessageArray[4],
                Host    = data.RawMessageArray[5],
                Server  = data.RawMessageArray[6],
                Nick    = data.RawMessageArray[7]
            };

            // HACK: realname field can be empty on bugged IRCds like InspIRCd-2.0
            // :topiary.voxanon.net 352 Mirco #anonplusradio CpGc igot.avhost Voxanon CpGc H
            if (data.MessageArray == null || data.MessageArray.Length < 2)
            {
                whoInfo.Realname = "";
            }
            else
            {
                string hopcountStr = data.MessageArray[0];
                if (Int32.TryParse(hopcountStr, out int hopcount))
                {
                    whoInfo.HopCount = hopcount;
                }
                else
                {
#if LOG4NET
                    Logger.MessageParser.Warn("Parse(): couldn't parse hopcount (as int): '" + hopcountStr + "'");
#endif
                }
                // skip hop count
                whoInfo.Realname = String.Join(" ", data.MessageArray, 1, data.MessageArray.Length - 1);
            }

            string usermode       = data.RawMessageArray[8];
            bool   owner          = false;
            bool   chanadmin      = false;
            bool   op             = false;
            bool   halfop         = false;
            bool   voice          = false;
            bool   ircop          = false;
            bool   away           = false;
            bool   registered     = false;
            int    usermodelength = usermode.Length;
            for (int i = 0; i < usermodelength; i++)
            {
                switch (usermode[i])
                {
                case 'H':
                    away = false;
                    break;

                case 'G':
                    away = true;
                    break;

                case '~':
                    owner = true;
                    break;

                case '&':
                    chanadmin = true;
                    break;

                case '@':
                    op = true;
                    break;

                case '%':
                    halfop = true;
                    break;

                case '+':
                    voice = true;
                    break;

                case '*':
                    ircop = true;
                    break;

                case 'r':
                    registered = true;
                    break;
                }
            }
            whoInfo.IsAway         = away;
            whoInfo.IsOwner        = owner;
            whoInfo.IsChannelAdmin = chanadmin;
            whoInfo.IsOp           = op;
            whoInfo.IsHalfop       = halfop;
            whoInfo.IsVoice        = voice;
            whoInfo.IsIrcOp        = ircop;

            whoInfo.IsRegistered = registered;

            return(whoInfo);
        }
 internal WhoEventArgs(IrcMessageData data, WhoInfo whoInfo) : base(data) => WhoInfo = whoInfo;