text: ////////////////////////////////////////////////////////////////////////////
  //
  // Copyright 2015 Realm Inc.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  // http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  //
  ////////////////////////////////////////////////////////////////////////////
  
  #ifndef BINDING_CONTEXT_HPP
  #define BINDING_CONTEXT_HPP
  
  #include "index_set.hpp"
  
  #include <tuple>
  #include <vector>
  
  namespace realm {
  // BindingContext is the extension point for adding binding-specific behavior to
  // a SharedRealm. It can be used to store additonal data associated with the
  // Realm which is needed by the binding, and there are several methods which
  // can be overridden to receive notifications of state changes within the Realm.
  //
  // A simple implementation which lets the user register functions to be
  // called on refresh could look like the following:
  //
  // class BindingContextImplementation : public BindingContext {
  // public:
  //     // A token returned from add_notification that can be used to remove the
  //     // notification later
  //     struct token : private std::list<std::function<void ()>>::iterator {
  //         token(std::list<std::function<void ()>>::iterator it) : std::list<std::function<void ()>>::iterator(it) { }
  //         friend class DelegateImplementation;
  //     };
  //
  //     token add_notification(std::function<void ()> func)
  //     {
  //         m_registered_notifications.push_back(std::move(func));
  //         return token(std::prev(m_registered_notifications.end()));
  //     }
  //
  //     void remove_notification(token entry)
  //     {
  //         m_registered_notifications.erase(entry);
  //     }
  //
  //     // Override the did_change method to call each registered notification
  //     void did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) override
  //     {
  //         // Loop oddly so that unregistering a notification from within the
  //         // registered function works
  //         for (auto it = m_registered_notifications.begin(); it != m_registered_notifications.end(); ) {
  //             (*it++)();
  //         }
  //     }
  //
  // private:
  //     std::list<std::function<void ()>> m_registered_notifications;
  // };
  class BindingContext {
  public:
      virtual ~BindingContext() = default;
  
      // If the user adds a notification handler to the Realm, will it ever
      // actually be called?
      virtual bool can_deliver_notifications() const noexcept { return true; }
  
      // Called by the Realm when a write transaction is committed to the file by
      // a different Realm instance (possibly in a different process)
      virtual void changes_available() { }
  
      struct ObserverState;
  
      // Override this function if you want to receive detailed information about
      // external changes to a specific set of objects.
      // This is called before each operation which may advance the read
      // transaction to include
      // ObserverStates for each row for which detailed change information is
      // desired.
      virtual std::vector<ObserverState> get_observed_rows() { return {}; }
  
      // Called immediately before the read transaction is advanced if detailed
      // change information was requested (by returning a non-empty array from
      // get_observed_rows()).
      // The observers vector is the vector returned by get_observed_row(),
      // updated with change information. The invalidated vector is a list of the
      // `info` fields of observed rows which will be deleted.
      virtual void will_change(std::vector<ObserverState> const& observers,
                               std::vector<void*> const& invalidated);
  
      // Called immediately after the read transaction version is advanced. Unlike
      // will_change(), this is called even if detailed change information was not
      // requested or if the Realm is not actually in a read transaction, although
      // both vectors will be empty in that case.
      virtual void did_change(std::vector<ObserverState> const& observers,
                              std::vector<void*> const& invalidated,
                              bool version_changed=true);
  
      // Change information for a single field of a row
      struct ColumnInfo {
          // The index of this column prior to the changes in the tracked
          // transaction, or -1 for newly inserted columns.
          size_t initial_column_index = -1;
          // What kind of change occurred?
          // Always Set or None for everything but LinkList columns.
          enum class Kind {
              None,   // No change
              Set,    // The value or entries at `indices` were assigned to
              Insert, // New values were inserted at each of the indices given
              Remove, // Values were removed at each of the indices given
              SetAll  // The entire LinkList has been replaced with a new set of values
          } kind = Kind::None;
          // The indices where things happened for Set, Insert and Remove on
          // LinkList columns. Not used for other types or for None or SetAll.
          IndexSet indices;
      };
  
      // Information about an observed row in a table
      //
      // Each object which needs detailed change information should have an
      // ObserverState entry in the vector returned from get_observed_rows(), with
      // the initial table and row indexes set (and optionally the info field).
      // The Realm parses the transaction log, and populates the `changes` vector
      // in each ObserverState with information about what changes were made.
      struct ObserverState {
          // Initial table and row which is observed
          // May be updated by row insertions and removals
          size_t table_ndx;
          size_t row_ndx;
  
          // Opaque userdata for the delegate's use
          void* info;
  
          // Populated with information about which columns were changed
          // May be shorter than the actual number of columns if the later columns
          // are not modified
          std::vector<ColumnInfo> changes;
  
          // Simple lexographic ordering
          friend bool operator<(ObserverState const& lft, ObserverState const& rgt)
          {
              return std::tie(lft.table_ndx, lft.row_ndx) < std::tie(rgt.table_ndx, rgt.row_ndx);
          }
      };
  };
  
  inline void BindingContext::will_change(std::vector<ObserverState> const&, std::vector<void*> const&) { }
  inline void BindingContext::did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) { }
  } // namespace realm
  
  #endif /* BINDING_CONTEXT_HPP */
  