text: /*
   *
   *    Copyright (c) 2016-2017 Nest Labs, Inc.
   *    All rights reserved.
   *
   *    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.
   */
  
  /**
   *    @file
   *      This file defines the generic Update Client for Weave
   *      Data Management (WDM) profile.
   *
   */
  
  #ifndef _WEAVE_DATA_MANAGEMENT_UPDATE_ENCODER_CURRENT_H
  #define _WEAVE_DATA_MANAGEMENT_UPDATE_ENCODER_CURRENT_H
  
  #if WEAVE_CONFIG_ENABLE_WDM_UPDATE
  
  #ifndef __STDC_FORMAT_MACROS
  #define __STDC_FORMAT_MACROS
  #endif // __STDC_FORMAT_MACROS
  
  #include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h>
  
  #include <Weave/Core/WeaveCore.h>
  #include <Weave/Profiles/data-management/MessageDef.h>
  #include <Weave/Profiles/data-management/EventLoggingTypes.h>
  #include <Weave/Profiles/data-management/TraitCatalog.h>
  
  namespace nl {
  namespace Weave {
  namespace Profiles {
  namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) {
  
  
  TraitUpdatableDataSink *Locate(TraitDataHandle aTraitDataHandle, const TraitCatalogBase<TraitDataSink> * aDataSinkCatalog);
  
  /**
   * This object encodes WDM UpdateRequest and PartialUpdateRequest payloads.
   * Note that both requests have the same format; they are differentiated only
   * by the message type, which is outside the scope of this object.
   *
   * The encoding is done synchronously by the EncodeRequest method.
   * The only other public method is InsertInProgressUpdateItem, which is
   * called by the SchemaEngine when it needs to push a dictionary back to the queue.
   */
  class UpdateEncoder
  {
  public:
      UpdateEncoder() { }
      ~UpdateEncoder() { }
  
      /**
       * This structure holds the I/O arguments to the EncodeRequest method.
       */
      struct Context
      {
          Context()
              : mBuf(NULL), mMaxPayloadSize(0), mUpdateRequestIndex(0), mExpiryTimeMicroSecond(0),
                      mItemInProgress(0), mInProgressUpdateList(NULL), mNextDictionaryElementPathHandle(kNullPropertyPathHandle),
                      mDataSinkCatalog(NULL), mNumDataElementsAddedToPayload(0)
          {  }
  
          // Destination buffer
          PacketBuffer *mBuf;                      /**< The output buffer. In case of failure the PacketBuffer's data length
                                                        is not updated, but the buffer contents are not preserved. */
          uint32_t mMaxPayloadSize;                /**< The maximum number of bytes to write. */
  
          // Other fields of the payload
          uint32_t mUpdateRequestIndex;            /**< The value of the UpdateRequestIndex field for this request. */
          utc_timestamp_t mExpiryTimeMicroSecond;  /**< The value of the ExpiryTimeMicroSecond field for this request.
                                                        It is encoded only if different than 0 */
  
          // What to encode
          size_t mItemInProgress;                  /**< Input: the index of the item of mInProgressUpdateList to start
                                                        encoding from.
                                                        Output: Upon returning, if the whole path list fit in the payload,
                                                        this field equals mInProgressUpdateList->GetPathStoreSize().
                                                        Otherwise, the index of the item to start the next payload from.*/
  
          TraitPathStore *mInProgressUpdateList;   /**< The list of TraitPaths to encode. */
          PropertyPathHandle mNextDictionaryElementPathHandle; /**< Input: if the encoding starts with a dictionary being
                                                                   resumed, this field holds the property path of the next
                                                                   dictionary item to encode. Otherwise, this field should be
                                                                   kNullPropertyPathHandle.
                                                                   Output: if the last DataElement encoded is a dictionary and
                                                                   not all items fit in the payload, this field holds the property
                                                                   path handle of the item to start from for the next payload. */
  
  
          const TraitCatalogBase<TraitDataSink> * mDataSinkCatalog; /**< Input: The catalog of TraitDataSinks which the
                                                                         TraitPaths refer to. */
  
          // Other
          size_t mNumDataElementsAddedToPayload;    /**< Output: The number of items encoded in the payload. */
      };
  
      WEAVE_ERROR EncodeRequest(Context &aContext);
  
      WEAVE_ERROR InsertInProgressUpdateItem(const TraitPath &aItem);
  
  private:
  
      /**
       * The context used to encode the path of a DataElement.
       */
      struct DataElementPathContext
      {
          DataElementPathContext()
              : mProfileId(0), mResourceId(0), mInstanceId(0), mNumTags(0),
                mTags(NULL), mSchemaVersionRange(NULL)
          { }
  
          uint32_t mProfileId;                    /**< Profile ID of the data sink. */
          ResourceIdentifier mResourceId;         /**< Resource ID of the data sink; if 0 it is not encoded
                                                       and defaults to the resource ID of the publisher. */
          uint64_t mInstanceId;                   /**< Instance ID of the data sink; if 0 it is not encoded
                                                       and defaults to the first instance of the trait in the publisher. */
          uint32_t mNumTags;                      /**< Number of tags in the tags array mTags. */
          uint64_t *mTags;                        /**< Array of tags to be encoded in the path. */
          const SchemaVersionRange * mSchemaVersionRange; /**< Schema version range. */
      };
  
      /**
       * The context used to encode the data of a DataElement.
       */
      struct DataElementDataContext
      {
          DataElementDataContext() :
              mTraitPath(0, 0),
              mUpdateRequiredVersion(0),
              mForceMerge(false),
              mDataSink(nullptr),
              mSchemaEngine(nullptr),
              mNextDictionaryElementPathHandle(0) {}
  
          TraitPath mTraitPath;                   /**< The TraitPath to encode. */
          DataVersion mUpdateRequiredVersion;     /**< If the update is conditional, the version the update is based off. */
          bool mForceMerge;                       /**< True if the property is a dictionary and should be encoded as a merge. */
          TraitUpdatableDataSink *mDataSink;      /**< DataSink the TraitPath refers to. */
          const TraitSchemaEngine *mSchemaEngine; /**< The TraitSchemaEngine of the data sink. */
          PropertyPathHandle mNextDictionaryElementPathHandle; /**< See @UpdateEncoder::Context */
      };
  
      WEAVE_ERROR EncodePreamble();
      WEAVE_ERROR EncodeDataList(void);
      WEAVE_ERROR EncodeDataElements();
      WEAVE_ERROR EncodeDataElement();
      static WEAVE_ERROR EncodeElementPath(const DataElementPathContext &aElementContext, TLV::TLVWriter &aWriter);
      static WEAVE_ERROR EncodeElementData(DataElementDataContext &aElementContext, TLV::TLVWriter &aWriter);
      WEAVE_ERROR EndUpdateRequest(void);
  
      void Checkpoint(TLV::TLVWriter &aWriter) { aWriter = mWriter; }
      void Rollback(TLV::TLVWriter &aWriter) { mWriter = aWriter; }
  
      static void RemoveInProgressPrivateItemsAfter(TraitPathStore &aList, size_t aItemInProgress);
  
      Context *mContext;
  
      TLV::TLVWriter mWriter;
      nl::Weave::TLV::TLVType mPayloadOuterContainerType, mDataListOuterContainerType, mDataElementOuterContainerType;
  
  };
  
  }; // namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current)
  }; // namespace Profiles
  }; // namespace Weave
  }; // namespace nl
  
  #endif // WEAVE_CONFIG_ENABLE_WDM_UPDATE
  
  #endif // _WEAVE_DATA_MANAGEMENT_UPDATE_ENCODER_CURRENT_H
  