/// <summary>
        /// Constructs ServerContactInfo representation of the server's contact information.
        /// </summary>
        /// <returns>ServerContactInfo structure or null if the function fails.</returns>
        public ServerContactInfo GetServerContactInfo()
        {
            log.Trace("()");

            ServerContactInfo res = new ServerContactInfo();

            res.NetworkId   = ProtocolHelper.ByteArrayToByteString(this.NetworkId);
            res.PrimaryPort = (uint)this.PrimaryPort;
            res.IpAddress   = ProtocolHelper.ByteArrayToByteString(this.IpAddress);

            log.Trace("(-)");
            return(res);
        }
        /// <summary>
        /// Creates ActivityQueryInformation representation of the activity.
        /// </summary>
        /// <param name="PrimaryServerContactInfo">Contact information to the activity's primary proximity server or null if this proximity server is the primary.</param>
        /// <returns>ActivityQueryInformation structure describing the activity.</returns>
        public ActivityQueryInformation ToActivityQueryInformation(ServerContactInfo PrimaryServerContactInfo)
        {
            bool isPrimary = this is PrimaryActivity;
            ActivityQueryInformation res = new ActivityQueryInformation()
            {
                SignedActivity = this.ToSignedActivityInformation(),
                IsPrimary      = isPrimary,
            };

            if (!isPrimary)
            {
                res.PrimaryServer = PrimaryServerContactInfo;
            }
            return(res);
        }
        /// <summary>
        /// Constructs server contact information.
        /// </summary>
        /// <param name="NetworkId">Identifier of the server.</param>
        /// <returns>Server contact information structure.</returns>
        public async Task <ServerContactInfo> GetServerContactInfoAsync(byte[] NetworkId)
        {
            log.Trace("(NetworkId:'{0}')", NetworkId);

            ServerContactInfo res = null;
            T remoteServer        = (await GetAsync(n => n.NetworkId == NetworkId, null, true)).FirstOrDefault();

            if (remoteServer != null)
            {
                res = remoteServer.GetServerContactInfo();
            }
            else
            {
                log.Error("{0} ID '{1}' not found.", remoteServer is Neighbor ? "Neighbor" : "Follower", NetworkId.ToHex());
            }

            log.Trace("(-):{0}", res != null ? "ServerContactInfo" : "null");
            return(res);
        }
        /// <summary>
        /// Validates ActivityInformation structure by itself. This function does not touch database and does not validate
        /// anything that depends on the context.
        /// </summary>
        /// <param name="Activity">Description of the activity to validate.</param>
        /// <param name="OwnerIdentityPublicKey">Public key of the activity's owner identity.</param>
        /// <param name="MessageBuilder">Network message builder of the client who sent this activity description.</param>
        /// <param name="RequestMessage">Full request message from client.</param>
        /// <param name="ErrorPrefix">Prefix to add to the validation error details.</param>
        /// <param name="ErrorResponse">If the function fails, this is filled with error response message that is ready to be sent to the client.</param>
        /// <returns>true if the activity information is valid, false otherwise.</returns>
        public static bool ValidateActivityInformation(ActivityInformation Activity, byte[] OwnerIdentityPublicKey, ProxMessageBuilder MessageBuilder, ProxProtocolMessage RequestMessage, string ErrorPrefix, out ProxProtocolMessage ErrorResponse)
        {
            log.Trace("()");

            bool res = false;

            ErrorResponse = null;
            string details = null;

            if (Activity == null)
            {
                Activity = new ActivityInformation();
            }
            if (Activity.ProfileServerContact == null)
            {
                Activity.ProfileServerContact = new ServerContactInfo();
            }

            SemVer version = new SemVer(Activity.Version);

            // Currently only supported version is 1.0.0.
            if (!version.Equals(SemVer.V100))
            {
                log.Debug("Unsupported version '{0}'.", version);
                details = "version";
            }


            if (details == null)
            {
                uint activityId = Activity.Id;

                // 0 is not a valid activity identifier.
                if (activityId == 0)
                {
                    log.Debug("Invalid activity ID '{0}'.", activityId);
                    details = "id";
                }
            }

            if (details == null)
            {
                byte[] pubKey      = Activity.OwnerPublicKey.ToByteArray();
                bool   pubKeyValid = (0 < pubKey.Length) && (pubKey.Length <= ProtocolHelper.MaxPublicKeyLengthBytes) && ByteArrayComparer.Equals(OwnerIdentityPublicKey, pubKey);
                if (!pubKeyValid)
                {
                    log.Debug("Invalid public key '{0}' does not match identity public key '{1}'.", pubKey.ToHex(), OwnerIdentityPublicKey.ToHex());
                    details = "ownerPublicKey";
                }
            }


            if (details == null)
            {
                ServerContactInfo sci    = Activity.ProfileServerContact;
                bool      networkIdValid = sci.NetworkId.Length == ProtocolHelper.NetworkIdentifierLength;
                IPAddress ipAddress      = IPAddressExtensions.IpFromBytes(sci.IpAddress.ToByteArray());
                bool      ipAddressValid = (ipAddress != null) && (Config.Configuration.TestModeEnabled || !ipAddress.IsReservedOrLocal());
                bool      portValid      = (1 <= sci.PrimaryPort) && (sci.PrimaryPort <= 65535);

                if (!networkIdValid || !ipAddressValid || !portValid)
                {
                    log.Debug("Profile server contact's network ID is {0}, IP address is {1}, port is {2}.", networkIdValid ? "valid" : "invalid", ipAddressValid ? "valid" : "invalid", portValid ? "valid" : "invalid");

                    if (!networkIdValid)
                    {
                        details = "profileServerContact.networkId";
                    }
                    else if (!ipAddressValid)
                    {
                        details = "profileServerContact.ipAddress";
                    }
                    else if (!portValid)
                    {
                        details = "profileServerContact.primaryPort";
                    }
                }
            }

            if (details == null)
            {
                string activityType = Activity.Type;
                if (activityType == null)
                {
                    activityType = "";
                }

                int  byteLen           = Encoding.UTF8.GetByteCount(activityType);
                bool activityTypeValid = (0 < byteLen) && (byteLen <= ProxMessageBuilder.MaxActivityTypeLengthBytes);
                if (!activityTypeValid)
                {
                    log.Debug("Activity type too long or zero length ({0} bytes, limit is {1}).", byteLen, ProxMessageBuilder.MaxActivityTypeLengthBytes);
                    details = "type";
                }
            }

            if (details == null)
            {
                GpsLocation locLat  = new GpsLocation(Activity.Latitude, 0);
                GpsLocation locLong = new GpsLocation(0, Activity.Longitude);
                if (!locLat.IsValid())
                {
                    log.Debug("Latitude '{0}' is not a valid GPS latitude value.", Activity.Latitude);
                    details = "latitude";
                }
                else if (!locLong.IsValid())
                {
                    log.Debug("Longitude '{0}' is not a valid GPS longitude value.", Activity.Longitude);
                    details = "longitude";
                }
            }

            if (details == null)
            {
                uint precision      = Activity.Precision;
                bool precisionValid = (0 <= precision) && (precision <= ProxMessageBuilder.MaxLocationPrecision);
                if (!precisionValid)
                {
                    log.Debug("Precision '{0}' is not an integer between 0 and {1}.", precision, ProxMessageBuilder.MaxLocationPrecision);
                    details = "precision";
                }
            }


            if (details == null)
            {
                DateTime?startTime      = ProtocolHelper.UnixTimestampMsToDateTime(Activity.StartTime);
                DateTime?expirationTime = ProtocolHelper.UnixTimestampMsToDateTime(Activity.ExpirationTime);

                if (startTime == null)
                {
                    log.Debug("Invalid activity start time timestamp '{0}'.", Activity.StartTime);
                    details = "startTime";
                }
                else if (expirationTime == null)
                {
                    log.Debug("Invalid activity expiration time timestamp '{0}'.", Activity.ExpirationTime);
                    details = "expirationTime";
                }
                else
                {
                    if (startTime > expirationTime)
                    {
                        log.Debug("Activity expiration time has to be greater than or equal to its start time.");
                        details = "expirationTime";
                    }
                    else if (expirationTime.Value > DateTime.UtcNow.AddHours(ActivityBase.MaxActivityLifeTimeHours))
                    {
                        log.Debug("Activity expiration time {0} is more than {1} hours in the future.", expirationTime.Value.ToString("yyyy-MM-dd HH:mm:ss"), ActivityBase.MaxActivityLifeTimeHours);
                        details = "expirationTime";
                    }
                }
            }

            if (details == null)
            {
                string extraData = Activity.ExtraData;
                if (extraData == null)
                {
                    extraData = "";
                }


                // Extra data is semicolon separated 'key=value' list, max ActivityBase.MaxActivityExtraDataLengthBytes bytes long.
                int byteLen = Encoding.UTF8.GetByteCount(extraData);
                if (byteLen > ProxMessageBuilder.MaxActivityExtraDataLengthBytes)
                {
                    log.Debug("Extra data too large ({0} bytes, limit is {1}).", byteLen, ProxMessageBuilder.MaxActivityExtraDataLengthBytes);
                    details = "extraData";
                }
            }

            if (details == null)
            {
                res = true;
            }
            else
            {
                ErrorResponse = MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, ErrorPrefix + details);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }