/**
         * Removes the given remote XBee device from the network.
         *
         * <p>Notice that this operation does not remove the remote XBee device
         * from the actual XBee network; it just tells the network object that it
         * will no longer contain that device. However, next time a discovery is
         * performed, it could be added again automatically.</p>
         *
         * <p>This method will check for a device that matches the 64-bit address
         * of the provided one, if found, that device will be removed from the
         * corresponding list. In case the 64-bit address is not defined, it will
         * use the 16-bit address for DigiMesh and ZigBee devices.</p>
         *
         * @param remoteDevice The remote device to be removed from the network.
         *
         * @throws ArgumentNullException if {@code RemoteDevice == null}.
         *
         * @see #addRemoteDevice(RemoteXBeeDevice)
         * @see #clearDeviceList()
         * @see RemoteXBeeDevice
         */
        public void removeRemoteDevice(RemoteXBeeDevice remoteDevice)
        {
            if (remoteDevice == null)
            {
                throw new ArgumentNullException("Remote device cannot be null.");
            }

            RemoteXBeeDevice devInNetwork = null;

            // Look in the 64-bit map.
            XBee64BitAddress addr64 = remoteDevice.Get64BitAddress();

            if (addr64 != null && !addr64.Equals(XBee64BitAddress.UNKNOWN_ADDRESS))
            {
                if (remotesBy64BitAddr.TryRemove(addr64, out devInNetwork))
                {
                    return;
                }
            }

            // If not found, look in the 16-bit map.
            XBee16BitAddress addr16 = Get16BitAddress(remoteDevice);

            if (addr16 != null && !addr16.Equals(XBee16BitAddress.UNKNOWN_ADDRESS))
            {
                // The preference order is:
                //    1.- Look in the 64-bit map
                //    2.- Then in the 16-bit map.
                // This should be maintained in the 'getDeviceBy16BitAddress' method.

                // Look for the 16-bit address in the 64-bit map.
                ICollection <RemoteXBeeDevice> devices = remotesBy64BitAddr.Values;
                foreach (RemoteXBeeDevice d in devices)
                {
                    XBee16BitAddress a = Get16BitAddress(d);
                    if (a != null && a.Equals(addr16))
                    {
                        RemoteXBeeDevice r;
                        remotesBy64BitAddr.TryRemove(d.Get64BitAddress(), out r);
                        return;
                    }
                }

                // If not found, look for the 16-bit address in the 16-bit map.
                // Remove the device.
                if (remotesBy16BitAddr.TryRemove(addr16, out devInNetwork))
                {
                    return;
                }
            }

            // If the device does not contain a valid address log an error.
            if ((addr64 == null || addr64.Equals(XBee64BitAddress.UNKNOWN_ADDRESS)) &&
                (addr16 == null || addr16.Equals(XBee16BitAddress.UNKNOWN_ADDRESS)))
            {
                logger.ErrorFormat("{0}Remote device '{1}' cannot be removed: 64-bit and 16-bit addresses must be specified.",
                                   localDevice.ToString(), remoteDevice.ToString());
            }
        }
        /**
         * Adds the given remote device to the network.
         *
         * <p>Notice that this operation does not join the remote XBee device to the
         * network; it just tells the network that it contains that device. However,
         * the device has only been added to the device list, and may not be
         * physically in the same network.</p>
         *
         * <p>The way of adding a device to the network is based on the 64-bit
         * address. If it is not configured:</p>
         *
         * <ul>
         * <li>For 802.15.4 and ZigBee devices, it will use the 16-bit address.</li>
         * <li>For the rest will return {@code false} as the result of the addition.</li>
         * </ul>
         *
         * @param remoteDevice The remote device to be added to the network.
         *
         * @return The remote XBee Device instance in the network, {@code null} if
         *         the device could not be successfully added.
         *
         * @throws ArgumentNullException if {@code RemoteDevice == null}.
         *
         * @see #addRemoteDevices(List)
         * @see #removeRemoteDevice(RemoteXBeeDevice)
         * @see RemoteXBeeDevice
         */
        public RemoteXBeeDevice addRemoteDevice(RemoteXBeeDevice remoteDevice)
        {
            if (remoteDevice == null)
            {
                throw new ArgumentNullException("Remote device cannot be null.");
            }

            logger.DebugFormat("{0}Adding device '{1}' to network.", localDevice.ToString(), remoteDevice.ToString());

            RemoteXBeeDevice devInNetwork = null;
            XBee64BitAddress addr64       = remoteDevice.Get64BitAddress();
            XBee16BitAddress addr16       = Get16BitAddress(remoteDevice);

            // Check if the device has 64-bit address.
            if (addr64 != null && !addr64.Equals(XBee64BitAddress.UNKNOWN_ADDRESS))
            {
                // The device has 64-bit address, so look in the 64-bit map.
                if (remotesBy64BitAddr.TryGetValue(addr64, out devInNetwork))
                {
                    // The device exists in the 64-bit map, so update the reference and return it.
                    logger.DebugFormat("{0}Existing device '{1}' in network.", localDevice.ToString(), devInNetwork.ToString());
                    devInNetwork.UpdateDeviceDataFrom(remoteDevice);
                    return(devInNetwork);
                }
                else
                {
                    // The device does not exist in the 64-bit map, so check its 16-bit address.
                    if (addr16 != null && !addr16.Equals(XBee16BitAddress.UNKNOWN_ADDRESS))
                    {
                        // The device has 16-bit address, so look in the 16-bit map.
                        if (remotesBy16BitAddr.TryGetValue(addr16, out devInNetwork))
                        {
                            // The device exists in the 16-bit map, so remove it and add it to the 64-bit map.
                            logger.DebugFormat("{0}Existing device '{1}' in network.", localDevice.ToString(), devInNetwork.ToString());
                            remotesBy16BitAddr.TryRemove(addr16, out devInNetwork);
                            devInNetwork.UpdateDeviceDataFrom(remoteDevice);
                            remotesBy64BitAddr.AddOrUpdate(addr64, devInNetwork, (k, v) => devInNetwork);
                            return(devInNetwork);
                        }
                        else
                        {
                            // The device does not exist in the 16-bit map, so add it to the 64-bit map.
                            remotesBy64BitAddr.AddOrUpdate(addr64, remoteDevice, (k, v) => remoteDevice);
                            return(remoteDevice);
                        }
                    }
                    else
                    {
                        // The device has not 16-bit address, so add it to the 64-bit map.
                        remotesBy64BitAddr.AddOrUpdate(addr64, remoteDevice, (k, v) => remoteDevice);
                        return(remoteDevice);
                    }
                }
            }

            // If the device has not 64-bit address, check if it has 16-bit address.
            if (addr16 != null && !addr16.Equals(XBee16BitAddress.UNKNOWN_ADDRESS))
            {
                // The device has 16-bit address, so look in the 64-bit map.
                ICollection <RemoteXBeeDevice> devices = remotesBy64BitAddr.Values;
                foreach (RemoteXBeeDevice d in devices)
                {
                    XBee16BitAddress a = Get16BitAddress(d);
                    if (a != null && a.Equals(addr16))
                    {
                        devInNetwork = d;
                        break;
                    }
                }
                // Check if the device exists in the 64-bit map.
                if (devInNetwork != null)
                {
                    // The device exists in the 64-bit map, so update the reference and return it.
                    logger.DebugFormat("{0}Existing device '{1}' in network.", localDevice.ToString(), devInNetwork.ToString());
                    devInNetwork.UpdateDeviceDataFrom(remoteDevice);
                    return(devInNetwork);
                }
                else
                {
                    // The device does not exist in the 64-bit map, so look in the 16-bit map.
                    devInNetwork = remotesBy16BitAddr[addr16];
                    if (devInNetwork != null)
                    {
                        // The device exists in the 16-bit map, so update the reference and return it.
                        logger.DebugFormat("{0}Existing device '{1}' in network.", localDevice.ToString(), devInNetwork.ToString());
                        devInNetwork.UpdateDeviceDataFrom(remoteDevice);
                        return(devInNetwork);
                    }
                    else
                    {
                        // The device does not exist in the 16-bit map, so add it.
                        remotesBy16BitAddr.AddOrUpdate(addr16, remoteDevice, (k, v) => remoteDevice);
                        return(remoteDevice);
                    }
                }
            }

            // If the device does not contain a valid address, return null.
            logger.ErrorFormat("{0}Remote device '{1}' cannot be added: 64-bit and 16-bit addresses must be specified.",
                               localDevice.ToString(), remoteDevice.ToString());
            return(null);
        }
 /*throws XBeeException */
 /**
  * Sends the provided data to the provided XBee device asynchronously.
  *
  * <p>Asynchronous transmissions do not wait for answer from the remote
  * device or for transmit status packet.</p>
  *
  * @param xbeeDevice The XBee device of the network that will receive the
  *                   data.
  * @param data Byte array containing the data to be sent.
  *
  * @throws InterfaceNotOpenException if this device connection is not open.
  * @throws ArgumentNullException if {@code xbeeDevice == null} or
  *                              if {@code data == null}.
  * @throws XBeeException if there is any XBee related exception.
  *
  * @see #sendData(RemoteXBeeDevice, byte[])
  * @see RemoteXBeeDevice
  */
 public void SendDataAsync(RemoteXBeeDevice xbeeDevice, byte[] data)
 {
     if (xbeeDevice == null)
         throw new ArgumentNullException("Remote XBee device cannot be null");
     SendDataAsync(xbeeDevice.Get64BitAddress(), data);
 }
        /*throws TimeoutException, XBeeException */
        /**
         * Sends the provided data to the given XBee device choosing the optimal
         * send method depending on the protocol of the local XBee device.
         *
         * <p>This method blocks till a success or error response arrives or the
         * configured receive timeout expires.</p>
         *
         * <p>The received timeout is configured using the {@code setReceiveTimeout}
         * method and can be consulted with {@code getReceiveTimeout} method.</p>
         *
         * <p>For non-blocking operations use the method
         * {@link #sendDataAsync(RemoteXBeeDevice, byte[])}.</p>
         *
         * @param xbeeDevice The XBee device of the network that will receive the
         *                   data.
         * @param data Byte array containing the data to be sent.
         *
         * @throws InterfaceNotOpenException if this device connection is not open.
         * @throws ArgumentNullException if {@code xbeeDevice == null} or
         *                              if {@code data == null}.
         * @throws TimeoutException if there is a timeout sending the data.
         * @throws XBeeException if there is any other XBee related exception.
         *
         * @see #getReceiveTimeout()
         * @see #setReceiveTimeout(int)
         * @see #sendDataAsync(RemoteXBeeDevice, byte[])
         */
        public void SendData(RemoteXBeeDevice xbeeDevice, byte[] data)
        {
            if (xbeeDevice == null)
                throw new ArgumentNullException("Remote XBee device cannot be null");

            switch (XBeeProtocol)
            {
                case XBeeProtocol.ZIGBEE:
                case XBeeProtocol.DIGI_POINT:
                    if (xbeeDevice.Get64BitAddress() != null && xbeeDevice.Get16BitAddress() != null)
                        SendData(xbeeDevice.Get64BitAddress(), xbeeDevice.Get16BitAddress(), data);
                    else
                        SendData(xbeeDevice.Get64BitAddress(), data);
                    break;
                case XBeeProtocol.RAW_802_15_4:
                    if (this is Raw802Device)
                    {
                        if (xbeeDevice.Get64BitAddress() != null)
                            ((Raw802Device)this).SendData(xbeeDevice.Get64BitAddress(), data);
                        else
                            ((Raw802Device)this).SendData(xbeeDevice.Get16BitAddress(), data);
                    }
                    else
                        SendData(xbeeDevice.Get64BitAddress(), data);
                    break;
                case XBeeProtocol.DIGI_MESH:
                default:
                    SendData(xbeeDevice.Get64BitAddress(), data);
                    break;
            }
        }
        /**
         * Removes the given remote XBee device from the network.
         *
         * <p>Notice that this operation does not remove the remote XBee device
         * from the actual XBee network; it just tells the network object that it
         * will no longer contain that device. However, next time a discovery is
         * performed, it could be added again automatically.</p>
         *
         * <p>This method will check for a device that matches the 64-bit address
         * of the provided one, if found, that device will be removed from the
         * corresponding list. In case the 64-bit address is not defined, it will
         * use the 16-bit address for DigiMesh and ZigBee devices.</p>
         *
         * @param remoteDevice The remote device to be removed from the network.
         *
         * @throws ArgumentNullException if {@code RemoteDevice == null}.
         *
         * @see #addRemoteDevice(RemoteXBeeDevice)
         * @see #clearDeviceList()
         * @see RemoteXBeeDevice
         */
        public void removeRemoteDevice(RemoteXBeeDevice remoteDevice)
        {
            if (remoteDevice == null)
                throw new ArgumentNullException("Remote device cannot be null.");

            RemoteXBeeDevice devInNetwork = null;

            // Look in the 64-bit map.
            XBee64BitAddress addr64 = remoteDevice.Get64BitAddress();
            if (addr64 != null && !addr64.Equals(XBee64BitAddress.UNKNOWN_ADDRESS))
            {
                if (remotesBy64BitAddr.TryRemove(addr64, out devInNetwork))
                    return;
            }

            // If not found, look in the 16-bit map.
            XBee16BitAddress addr16 = Get16BitAddress(remoteDevice);
            if (addr16 != null && !addr16.Equals(XBee16BitAddress.UNKNOWN_ADDRESS))
            {

                // The preference order is:
                //    1.- Look in the 64-bit map
                //    2.- Then in the 16-bit map.
                // This should be maintained in the 'getDeviceBy16BitAddress' method.

                // Look for the 16-bit address in the 64-bit map.
                ICollection<RemoteXBeeDevice> devices = remotesBy64BitAddr.Values;
                foreach (RemoteXBeeDevice d in devices)
                {
                    XBee16BitAddress a = Get16BitAddress(d);
                    if (a != null && a.Equals(addr16))
                    {
                        RemoteXBeeDevice r;
                        remotesBy64BitAddr.TryRemove(d.Get64BitAddress(), out r);
                        return;
                    }
                }

                // If not found, look for the 16-bit address in the 16-bit map.
                // Remove the device.
                if (remotesBy16BitAddr.TryRemove(addr16, out devInNetwork))
                    return;
            }

            // If the device does not contain a valid address log an error.
            if ((addr64 == null || addr64.Equals(XBee64BitAddress.UNKNOWN_ADDRESS))
                    && (addr16 == null || addr16.Equals(XBee16BitAddress.UNKNOWN_ADDRESS)))
                logger.ErrorFormat("{0}Remote device '{1}' cannot be removed: 64-bit and 16-bit addresses must be specified.",
                        localDevice.ToString(), remoteDevice.ToString());
        }
        /**
         * Adds the given remote device to the network.
         *
         * <p>Notice that this operation does not join the remote XBee device to the
         * network; it just tells the network that it contains that device. However,
         * the device has only been added to the device list, and may not be
         * physically in the same network.</p>
         *
         * <p>The way of adding a device to the network is based on the 64-bit
         * address. If it is not configured:</p>
         *
         * <ul>
         * <li>For 802.15.4 and ZigBee devices, it will use the 16-bit address.</li>
         * <li>For the rest will return {@code false} as the result of the addition.</li>
         * </ul>
         *
         * @param remoteDevice The remote device to be added to the network.
         *
         * @return The remote XBee Device instance in the network, {@code null} if
         *         the device could not be successfully added.
         *
         * @throws ArgumentNullException if {@code RemoteDevice == null}.
         *
         * @see #addRemoteDevices(List)
         * @see #removeRemoteDevice(RemoteXBeeDevice)
         * @see RemoteXBeeDevice
         */
        public RemoteXBeeDevice addRemoteDevice(RemoteXBeeDevice remoteDevice)
        {
            if (remoteDevice == null)
                throw new ArgumentNullException("Remote device cannot be null.");

            logger.DebugFormat("{0}Adding device '{1}' to network.", localDevice.ToString(), remoteDevice.ToString());

            RemoteXBeeDevice devInNetwork = null;
            XBee64BitAddress addr64 = remoteDevice.Get64BitAddress();
            XBee16BitAddress addr16 = Get16BitAddress(remoteDevice);

            // Check if the device has 64-bit address.
            if (addr64 != null && !addr64.Equals(XBee64BitAddress.UNKNOWN_ADDRESS))
            {
                // The device has 64-bit address, so look in the 64-bit map.
                if (remotesBy64BitAddr.TryGetValue(addr64, out devInNetwork))
                {
                    // The device exists in the 64-bit map, so update the reference and return it.
                    logger.DebugFormat("{0}Existing device '{1}' in network.", localDevice.ToString(), devInNetwork.ToString());
                    devInNetwork.UpdateDeviceDataFrom(remoteDevice);
                    return devInNetwork;
                }
                else
                {
                    // The device does not exist in the 64-bit map, so check its 16-bit address.
                    if (addr16 != null && !addr16.Equals(XBee16BitAddress.UNKNOWN_ADDRESS))
                    {
                        // The device has 16-bit address, so look in the 16-bit map.
                        if (remotesBy16BitAddr.TryGetValue(addr16, out devInNetwork))
                        {
                            // The device exists in the 16-bit map, so remove it and add it to the 64-bit map.
                            logger.DebugFormat("{0}Existing device '{1}' in network.", localDevice.ToString(), devInNetwork.ToString());
                            remotesBy16BitAddr.TryRemove(addr16, out devInNetwork);
                            devInNetwork.UpdateDeviceDataFrom(remoteDevice);
                            remotesBy64BitAddr.AddOrUpdate(addr64, devInNetwork, (k, v) => devInNetwork);
                            return devInNetwork;
                        }
                        else
                        {
                            // The device does not exist in the 16-bit map, so add it to the 64-bit map.
                            remotesBy64BitAddr.AddOrUpdate(addr64, remoteDevice, (k, v) => remoteDevice);
                            return remoteDevice;
                        }
                    }
                    else
                    {
                        // The device has not 16-bit address, so add it to the 64-bit map.
                        remotesBy64BitAddr.AddOrUpdate(addr64, remoteDevice, (k, v) => remoteDevice);
                        return remoteDevice;
                    }
                }
            }

            // If the device has not 64-bit address, check if it has 16-bit address.
            if (addr16 != null && !addr16.Equals(XBee16BitAddress.UNKNOWN_ADDRESS))
            {
                // The device has 16-bit address, so look in the 64-bit map.
                ICollection<RemoteXBeeDevice> devices = remotesBy64BitAddr.Values;
                foreach (RemoteXBeeDevice d in devices)
                {
                    XBee16BitAddress a = Get16BitAddress(d);
                    if (a != null && a.Equals(addr16))
                    {
                        devInNetwork = d;
                        break;
                    }
                }
                // Check if the device exists in the 64-bit map.
                if (devInNetwork != null)
                {
                    // The device exists in the 64-bit map, so update the reference and return it.
                    logger.DebugFormat("{0}Existing device '{1}' in network.", localDevice.ToString(), devInNetwork.ToString());
                    devInNetwork.UpdateDeviceDataFrom(remoteDevice);
                    return devInNetwork;
                }
                else
                {
                    // The device does not exist in the 64-bit map, so look in the 16-bit map.
                    devInNetwork = remotesBy16BitAddr[addr16];
                    if (devInNetwork != null)
                    {
                        // The device exists in the 16-bit map, so update the reference and return it.
                        logger.DebugFormat("{0}Existing device '{1}' in network.", localDevice.ToString(), devInNetwork.ToString());
                        devInNetwork.UpdateDeviceDataFrom(remoteDevice);
                        return devInNetwork;
                    }
                    else
                    {
                        // The device does not exist in the 16-bit map, so add it.
                        remotesBy16BitAddr.AddOrUpdate(addr16, remoteDevice, (k, v) => remoteDevice);
                        return remoteDevice;
                    }
                }
            }

            // If the device does not contain a valid address, return null.
            logger.ErrorFormat("{0}Remote device '{1}' cannot be added: 64-bit and 16-bit addresses must be specified.",
                    localDevice.ToString(), remoteDevice.ToString());
            return null;
        }