[ndnSIM] face: Emulation mode of ndn::Face

Change-Id: Ib345978575104afa436296d2f5a824a39e3a9780
diff --git a/src/detail/face-impl.hpp b/src/detail/face-impl.hpp
index fb6642a..5ebec16 100644
--- a/src/detail/face-impl.hpp
+++ b/src/detail/face-impl.hpp
@@ -40,6 +40,13 @@
 #include "../management/nfd-controller.hpp"
 #include "../management/nfd-command-options.hpp"
 
+#include <ns3/ptr.h>
+#include <ns3/node.h>
+#include <ns3/node-list.h>
+#include <ns3/ndnSIM/model/ndn-l3-protocol.hpp>
+
+#include "ns3/ndnSIM/NFD/daemon/face/local-face.hpp"
+
 namespace ndn {
 
 class Face::Impl : noncopyable
@@ -49,29 +56,80 @@
   typedef std::list<shared_ptr<InterestFilterRecord> > InterestFilterTable;
   typedef ContainerWithOnEmptySignal<shared_ptr<RegisteredPrefix>> RegisteredPrefixTable;
 
+  class NfdFace : public ::nfd::LocalFace
+  {
+  public:
+    NfdFace(Impl& face, const ::nfd::FaceUri& localUri, const ::nfd::FaceUri& remoteUri)
+      : ::nfd::LocalFace(localUri, remoteUri)
+      , m_appFaceImpl(face)
+    {
+    }
+
+  public: // from ::nfd::LocalFace
+    /**
+     * @brief Send Interest towards application
+     */
+    virtual void
+    sendInterest(const Interest& interest)
+    {
+      NS_LOG_DEBUG("<< Interest " << interest);
+      shared_ptr<const Interest> interestPtr = interest.shared_from_this();
+      m_appFaceImpl.m_scheduler.scheduleEvent(time::seconds(0), [this, interestPtr] {
+          m_appFaceImpl.processInterestFilters(*interestPtr);
+        });
+    }
+
+    /**
+     * @brief Send Data towards application
+     */
+    virtual void
+    sendData(const Data& data)
+    {
+      NS_LOG_DEBUG("<< Data " << data.getName());
+      shared_ptr<const Data> dataPtr = data.shared_from_this();
+      m_appFaceImpl.m_scheduler.scheduleEvent(time::seconds(0), [this, dataPtr] {
+          m_appFaceImpl.satisfyPendingInterests(*dataPtr);
+        });
+    }
+
+    /** \brief Close the face
+     *
+     *  This terminates all communication on the face and cause
+     *  onFail() method event to be invoked
+     */
+    virtual void
+    close()
+    {
+      this->fail("close");
+    }
+
+  private:
+    friend class Impl;
+    Impl& m_appFaceImpl;
+  };
+
+  ////////////////////////////////////////////////////////////////////////
+
   explicit
   Impl(Face& face)
     : m_face(face)
     , m_scheduler(m_face.getIoService())
-    , m_processEventsTimeoutEvent(m_scheduler)
   {
-    auto postOnEmptyPitOrNoRegisteredPrefixes = [this] {
-      this->m_face.getIoService().post(bind(&Impl::onEmptyPitOrNoRegisteredPrefixes, this));
-      // without this extra "post", transport can get paused (-async_read) and then resumed
-      // (+async_read) from within onInterest/onData callback.  After onInterest/onData
-      // finishes, there is another +async_read with the same memory block.  A few of such
-      // async_read duplications can cause various effects and result in segfault.
-    };
+    ns3::Ptr<ns3::Node> node = ns3::NodeList::GetNode(ns3::Simulator::GetContext());
+    NS_ASSERT_MSG(node->GetObject<ns3::ndn::L3Protocol>() != 0,
+                  "NDN stack should be installed on the node " << node);
 
-    m_pendingInterestTable.onEmpty.connect(postOnEmptyPitOrNoRegisteredPrefixes);
-    m_registeredPrefixTable.onEmpty.connect(postOnEmptyPitOrNoRegisteredPrefixes);
+    auto uri = ::nfd::FaceUri("ndnFace://" + boost::lexical_cast<std::string>(node->GetId()));
+    m_nfdFace = make_shared<NfdFace>(*this, uri, uri);
+
+    node->GetObject<ns3::ndn::L3Protocol>()->addFace(m_nfdFace);
   }
 
   /////////////////////////////////////////////////////////////////////////////////////////////////
   /////////////////////////////////////////////////////////////////////////////////////////////////
 
   void
-  satisfyPendingInterests(Data& data)
+  satisfyPendingInterests(const Data& data)
   {
     for (auto entry = m_pendingInterestTable.begin(); entry != m_pendingInterestTable.end(); ) {
       if ((*entry)->getInterest().matchesData(data)) {
@@ -87,7 +145,7 @@
   }
 
   void
-  processInterestFilters(Interest& interest)
+  processInterestFilters(const Interest& interest)
   {
     for (const auto& filter : m_interestFilterTable) {
       if (filter->doesMatch(interest.getName())) {
@@ -100,37 +158,16 @@
   /////////////////////////////////////////////////////////////////////////////////////////////////
 
   void
-  ensureConnected(bool wantResume)
-  {
-    if (!m_face.m_transport->isConnected())
-      m_face.m_transport->connect(m_face.m_ioService,
-                                  bind(&Face::onReceiveElement, &m_face, _1));
-
-    if (wantResume && !m_face.m_transport->isExpectingData())
-      m_face.m_transport->resume();
-  }
-
-  void
   asyncExpressInterest(const shared_ptr<const Interest>& interest,
                        const OnData& onData, const OnTimeout& onTimeout)
   {
-    this->ensureConnected(true);
-
     auto entry =
       m_pendingInterestTable.insert(make_shared<PendingInterest>(interest,
                                                                  onData, onTimeout,
                                                                  ref(m_scheduler))).first;
     (*entry)->setDeleter([this, entry] { m_pendingInterestTable.erase(entry); });
 
-    if (!interest->getLocalControlHeader().empty(nfd::LocalControlHeader::ENCODE_NEXT_HOP)) {
-      // encode only NextHopFaceId towards the forwarder
-      m_face.m_transport->send(interest->getLocalControlHeader()
-                               .wireEncode(*interest, nfd::LocalControlHeader::ENCODE_NEXT_HOP),
-                               interest->wireEncode());
-    }
-    else {
-      m_face.m_transport->send(interest->wireEncode());
-    }
+    m_nfdFace->emitSignal(onReceiveInterest, *interest);
   }
 
   void
@@ -142,17 +179,7 @@
   void
   asyncPutData(const shared_ptr<const Data>& data)
   {
-    this->ensureConnected(true);
-
-    if (!data->getLocalControlHeader().empty(nfd::LocalControlHeader::ENCODE_CACHING_POLICY)) {
-      m_face.m_transport->send(
-        data->getLocalControlHeader().wireEncode(*data,
-                                                 nfd::LocalControlHeader::ENCODE_CACHING_POLICY),
-        data->wireEncode());
-    }
-    else {
-      m_face.m_transport->send(data->wireEncode());
-    }
+    m_nfdFace->emitSignal(onReceiveData, *data);
   }
 
   /////////////////////////////////////////////////////////////////////////////////////////////////
@@ -266,27 +293,15 @@
     }
   }
 
-  void
-  onEmptyPitOrNoRegisteredPrefixes()
-  {
-    if (m_pendingInterestTable.empty() && m_registeredPrefixTable.empty()) {
-      m_face.m_transport->pause();
-      if (!m_ioServiceWork) {
-        m_processEventsTimeoutEvent.cancel();
-      }
-    }
-  }
-
 private:
   Face& m_face;
   util::Scheduler m_scheduler;
-  util::scheduler::ScopedEventId m_processEventsTimeoutEvent;
 
   PendingInterestTable m_pendingInterestTable;
   InterestFilterTable m_interestFilterTable;
   RegisteredPrefixTable m_registeredPrefixTable;
 
-  unique_ptr<boost::asio::io_service::work> m_ioServiceWork; // if thread needs to be preserved
+  shared_ptr<NfdFace> m_nfdFace;
 
   friend class Face;
 };
diff --git a/src/detail/pending-interest.hpp b/src/detail/pending-interest.hpp
index 41f6b37..9cea52c 100644
--- a/src/detail/pending-interest.hpp
+++ b/src/detail/pending-interest.hpp
@@ -76,9 +76,9 @@
    * @note If the DataCallback is an empty function, this method does nothing.
    */
   void
-  invokeDataCallback(Data& data)
+  invokeDataCallback(const Data& data)
   {
-    m_onData(*m_interest, data);
+    m_onData(*m_interest, const_cast<Data&>(data));
   }
 
   /**