text: /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
  /**
   * Copyright (c) 2011-2015  Regents of the University of California.
   *
   * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
   * contributors.
   *
   * ndnSIM is free software: you can redistribute it and/or modify it under the terms
   * of the GNU General Public License as published by the Free Software Foundation,
   * either version 3 of the License, or (at your option) any later version.
   *
   * ndnSIM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
   * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
   * PURPOSE.  See the GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License along with
   * ndnSIM, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
   **/
  
  #ifndef NDN_CONTENT_STORE_IMPL_H_
  #define NDN_CONTENT_STORE_IMPL_H_
  
  #include "ns3/ndnSIM/model/ndn-common.hpp"
  
  #include "ndn-content-store.hpp"
  
  #include "ns3/packet.h"
  #include <boost/foreach.hpp>
  
  #include "ns3/log.h"
  #include "ns3/uinteger.h"
  #include "ns3/string.h"
  
  #include "../../utils/trie/trie-with-policy.hpp"
  
  namespace ns3 {
  namespace ndn {
  namespace cs {
  
  /**
   * @ingroup ndn-cs
   * @brief Cache entry implementation with additional references to the base container
   */
  template<class CS>
  class EntryImpl : public Entry {
  public:
    typedef Entry base_type;
  
  public:
    EntryImpl(Ptr<ContentStore> cs, shared_ptr<const Data> data)
      : Entry(cs, data)
      , item_(0)
    {
    }
  
    void
    SetTrie(typename CS::super::iterator item)
    {
      item_ = item;
    }
  
    typename CS::super::iterator
    to_iterator()
    {
      return item_;
    }
    typename CS::super::const_iterator
    to_iterator() const
    {
      return item_;
    }
  
  private:
    typename CS::super::iterator item_;
  };
  
  /**
   * @ingroup ndn-cs
   * @brief Base implementation of NDN content store
   */
  template<class Policy>
  class ContentStoreImpl
    : public ContentStore,
      protected ndnSIM::
        trie_with_policy<Name,
                         ndnSIM::smart_pointer_payload_traits<EntryImpl<ContentStoreImpl<Policy>>,
                                                              Entry>,
                         Policy> {
  public:
    typedef ndnSIM::
      trie_with_policy<Name, ndnSIM::smart_pointer_payload_traits<EntryImpl<ContentStoreImpl<Policy>>,
                                                                  Entry>,
                       Policy> super;
  
    typedef EntryImpl<ContentStoreImpl<Policy>> entry;
  
    static TypeId
    GetTypeId();
  
    ContentStoreImpl(){};
    virtual ~ContentStoreImpl(){};
  
    // from ContentStore
  
    virtual inline shared_ptr<Data>
    Lookup(shared_ptr<const Interest> interest);
  
    virtual inline bool
    Add(shared_ptr<const Data> data);
  
    // virtual bool
    // Remove (shared_ptr<Interest> header);
  
    virtual inline void
    Print(std::ostream& os) const;
  
    virtual uint32_t
    GetSize() const;
  
    virtual Ptr<Entry>
    Begin();
  
    virtual Ptr<Entry>
    End();
  
    virtual Ptr<Entry> Next(Ptr<Entry>);
  
    const typename super::policy_container&
    GetPolicy() const
    {
      return super::getPolicy();
    }
  
    typename super::policy_container&
    GetPolicy()
    {
      return super::getPolicy();
    }
  
  //private:
    void
    SetMaxSize(uint32_t maxSize);
  
    uint32_t
    GetMaxSize() const;
  
  private:
    static LogComponent g_log; ///< @brief Logging variable
  
    /// @brief trace of for entry additions (fired every time entry is successfully added to the
    /// cache): first parameter is pointer to the CS entry
    TracedCallback<Ptr<const Entry>> m_didAddEntry;
  };
  
  //////////////////////////////////////////
  ////////// Implementation ////////////////
  //////////////////////////////////////////
  
  template<class Policy>
  LogComponent
    ContentStoreImpl<Policy>::g_log = LogComponent(("ndn.cs." + Policy::GetName()).c_str());
  
  template<class Policy>
  TypeId
  ContentStoreImpl<Policy>::GetTypeId()
  {
    static TypeId tid =
      TypeId(("ns3::ndn::cs::" + Policy::GetName()).c_str())
        .SetGroupName("Ndn")
        .SetParent<ContentStore>()
        .AddConstructor<ContentStoreImpl<Policy>>()
        .AddAttribute("MaxSize",
                      "Set maximum number of entries in ContentStore. If 0, limit is not enforced",
                      StringValue("100"), MakeUintegerAccessor(&ContentStoreImpl<Policy>::GetMaxSize,
                                                               &ContentStoreImpl<Policy>::SetMaxSize),
                      MakeUintegerChecker<uint32_t>())
  
        .AddTraceSource("DidAddEntry",
                        "Trace fired every time entry is successfully added to the cache",
                        MakeTraceSourceAccessor(&ContentStoreImpl<Policy>::m_didAddEntry));
  
    return tid;
  }
  
  struct isNotExcluded {
    inline isNotExcluded(const Exclude& exclude)
      : m_exclude(exclude)
    {
    }
  
    bool
    operator()(const name::Component& comp) const
    {
      return !m_exclude.isExcluded(comp);
    }
  
  private:
    const Exclude& m_exclude;
  };
  
  template<class Policy>
  shared_ptr<Data>
  ContentStoreImpl<Policy>::Lookup(shared_ptr<const Interest> interest)
  {
    NS_LOG_FUNCTION(this << interest->getName());
  
    typename super::const_iterator node;
    if (interest->getExclude().empty()) {
      node = this->deepest_prefix_match(interest->getName());
    }
    else {
      node = this->deepest_prefix_match_if_next_level(interest->getName(),
                                                      isNotExcluded(interest->getExclude()));
    }
  
    if (node != this->end()) {
      this->m_cacheHitsTrace(interest, node->payload()->GetData());
  
      shared_ptr<Data> copy = make_shared<Data>(*node->payload()->GetData());
      return copy;
    }
    else {
      this->m_cacheMissesTrace(interest);
      return 0;
    }
  }
  
  /**
  * @brief Add data to cache
  */
  template<class Policy>
  bool
  ContentStoreImpl<Policy>::Add(shared_ptr<const Data> data)
  {
    NS_LOG_FUNCTION(this << data->getName());
  
    Ptr<entry> newEntry = Create<entry>(this, data);
    std::pair<typename super::iterator, bool> result = super::insert(data->getName(), newEntry);
  
    if (result.first != super::end()) {
      if (result.second) {
        newEntry->SetTrie(result.first);
  
        m_didAddEntry(newEntry);
        return true;
      }
      else {
        // should we do anything?
        // update payload? add new payload?
        return false;
      }
    }
    else
      return false; // cannot insert entry
  }
  
  template<class Policy>
  void
  ContentStoreImpl<Policy>::Print(std::ostream& os) const
  {
    for (typename super::policy_container::const_iterator item = this->getPolicy().begin();
         item != this->getPolicy().end(); item++) {
      os << item->payload ()->GetName () << std::endl;
    }
  }
  
  template<class Policy>
  void
  ContentStoreImpl<Policy>::SetMaxSize(uint32_t maxSize)
  {
    this->getPolicy().set_max_size(maxSize);
  }
  
  template<class Policy>
  uint32_t
  ContentStoreImpl<Policy>::GetMaxSize() const
  {
    return this->getPolicy().get_max_size();
  }
  
  template<class Policy>
  uint32_t
  ContentStoreImpl<Policy>::GetSize() const
  {
    return this->getPolicy().size();
  }
  
  /**
  * @brief Gets the first item in a cache?
  */
  template<class Policy>
  Ptr<Entry>
  ContentStoreImpl<Policy>::Begin()
  {
    typename super::parent_trie::recursive_iterator item(super::getTrie()), end(0);
    for (; item != end; item++) {
      if (item->payload() == 0)
        continue;
      break;
    }
  
    if (item == end)
      return End();
    else
      return item->payload();
  }
  
  /**
  * @brief Method that always returns 0 - fairly useless! Perhaps used in subclass
  */
  template<class Policy>
  Ptr<Entry>
  ContentStoreImpl<Policy>::End()
  {
    return 0;
  }
  
  /**
  * @brief Gets next content-store entry
  * It does so by iterating through them until it finds a non-zero payload. It requires a starting place, 'from' where it starts iterating
  */
  template<class Policy>
  Ptr<Entry>
  ContentStoreImpl<Policy>::Next(Ptr<Entry> from)
  {
    if (from == 0)
      return 0;
  
    // Call the Super classes parent_trie (which uses a recursive iterator?) and get the item at entry 'from'
    typename super::parent_trie::recursive_iterator item(*StaticCast<entry>(from)->to_iterator()),
      end(0);
  
    // Iterate over the items until you find one with a non-zero payload
    for (item++; item != end; item++) {
      if (item->payload() == 0)
        continue;
      break;
    }
  
    // If you are at the end of the cache return End? otherwise return the payload
    if (item == end)
      return End();
    else
      return item->payload();
  }
  
  } // namespace cs
  } // namespace ndn
  } // namespace ns3
  
  #endif // NDN_CONTENT_STORE_IMPL_H_
  