public void Constructor_ValuesInitialized()
        {
            var type = LinkLayerResultType.Success;
            var uut  = new LinkLayerResult(type);

            Assert.AreEqual(type, uut.Result);
        }
        public void Constructor_Explicit_ValuesInitialized()
        {
            var nb  = "neighbourName";
            var msg = new Message(new DataMsg(2), "destination", "source", 1);

            var uut = new LinkLayerResult(msg, nb);

            Assert.AreEqual(nb, uut.MissingNeighbour);
            Assert.AreEqual(msg, uut.DroppedMessage);
            Assert.AreEqual(LinkLayerResultType.NextHopNotFound, uut.Result);
        }
        /// <summary>
        /// Sends a new DATA message. Looks up the next-hop in the routing table, and passes message to link-layer.
        /// </summary>
        /// <param name="msg"></param>
        public void SendDataMessage(Message msg)
        {
            var outgoingMessage = new Message(msg);

            if (outgoingMessage.Type != MessageType.Data)
            {
                throw new ArgumentException("Sorry mate, DATA-messages only.");
            }

            LinkLayerResult result = null;

            //If this is a broadcast-packet, no table-lookup is necessary (and will fail)
            if (outgoingMessage.DestinationAddress == SimulationConstants.BroadcastAddress)             //RREQs will always fall into this one, even those processed by me, since destination=broadcast
            {
                result = _linkLayer.SendMessage(outgoingMessage, SimulationConstants.BroadcastAddress); //Broadcasts cannot miss a neighbour
            }

            //If an active route valid for forwarding exists, find next-hop and send message
            if (_aodvHelper.RoutingTable.ActiveRouteExists(outgoingMessage.DestinationAddress))
            {
                //Find route to destination, to acquire next-hop
                var destinationEntry = _aodvHelper.RoutingTable.GetEntry(outgoingMessage.DestinationAddress);

                //Update lifetime of active route to destination when it's used per section 6.2 ending.
                _aodvHelper.RoutingTable.SetRouteActive(destinationEntry.DestinationAddress);

                //Update lifetime of route to next-hop per section 6.2 ending.
                var nextHopRoute = _aodvHelper.RoutingTable.GetEntry(destinationEntry.NextHop);
                if (nextHopRoute != null)
                {
                    _aodvHelper.RoutingTable.SetRouteActive(nextHopRoute.DestinationAddress);
                }

                //Update lifetime of route to originator of data (if it was not sent by me) per section 6.2 ending.
                if (outgoingMessage.SourceAddress != _localAddress)
                {
                    var sourceRoute = _aodvHelper.RoutingTable.GetEntry(outgoingMessage.SourceAddress);
                    if (sourceRoute != null)
                    {
                        _aodvHelper.RoutingTable.SetRouteActive(sourceRoute.DestinationAddress);
                    }
                }

                //Update lifetime of route to previous hop (if it was not sent by me) per section 6.2 ending.
                if (outgoingMessage.PreviousHop != _localAddress)
                {
                    var previousHopRoute = _aodvHelper.RoutingTable.GetEntry(outgoingMessage.PreviousHop);
                    if (previousHopRoute != null)
                    {
                        _aodvHelper.RoutingTable.SetRouteActive(previousHopRoute.DestinationAddress);
                    }
                }

                result = _linkLayer.SendMessage(outgoingMessage, destinationEntry.NextHop); //Send message along route towards destination. If it fails, routes updated above are invalidated, no worries.
            }
            //If active route does not exist, but a RREQ has been sent for this destination and are not expired yet
            else if (!_aodvHelper.RoutingTable.ActiveRouteExists(outgoingMessage.DestinationAddress) && _aodvHelper.RreqAttemptExist(outgoingMessage.DestinationAddress, _localAddress))
            {
                ParkMessage(new Message(outgoingMessage)); //Park message and wait for RREP
                return;
            }
            //If any kind of route does NOT exist, OR existing route is invalid/expired, send a new RREQ for the destination
            else if (!_aodvHelper.RoutingTable.RouteExists(outgoingMessage.DestinationAddress) || (!_aodvHelper.RoutingTable.GetEntry(outgoingMessage.DestinationAddress)?.Valid ?? true))
            {
                ParkMessage(new Message(outgoingMessage));
                _statistics.RoutingRequestGenerated();

                var rreqMsg = _aodvHelper.GenerateInitialRreqMessage(outgoingMessage.DestinationAddress);
                _statistics.AddAodvMessageSent(rreqMsg);
                result = _linkLayer.SendMessage(rreqMsg, SimulationConstants.BroadcastAddress); //No need to check for missing neighbour in result
            }

            if (result.Result == LinkLayerResultType.NextHopNotFound)
            {
                //This is a clear Section 6.11 (i): Link break for next hop of active route while transmiting data (hehe)
                //_statistics.AddMessageDropped(e.DroppedMessage);
                _statistics.AddDataMessageDroppedNextHopUnavailable(result.DroppedMessage);

                //Get a list of all unreachable destinations, because of this link-failure. This also updates and invalidates entries in routing tables.
                var unreachableDestinations = _aodvHelper.RoutingTable.HandleAndGetUnreachableDestinations(result.MissingNeighbour);
                var rerr = new Rerr(unreachableDestinations);

                //If any destinations were affected by this link-failure
                if (unreachableDestinations.Count > 0)
                {
                    //Get all nodes that use the now missing link as a part of their route
                    var precursors = _aodvHelper.RoutingTable.GetEntry(unreachableDestinations[0].UnreachableDestinationAddress).Precursors;

                    foreach (var precursor in precursors)
                    {
                        //Send RERR to precursors if any is available
                        var rerrMsg = new Message(new Rerr(rerr), precursors[0], _localAddress, _conf.MessageTtlValue);
                        SendAodvMessage(rerrMsg, precursor);
                    }
                }
            }
        }