/* ****************************************************************************
*
* setAttrs -
*/
static void setAttrs(const SubscriptionUpdate& subUp, const BSONObj& subOrig, BSONObjBuilder* b)
{
  if (subUp.notificationProvided)
  {
    setAttrs(subUp, b);
  }
  else
  {
    BSONArray attrs = getArrayFieldF(subOrig, CSUB_ATTRS);

    b->append(CSUB_ATTRS, attrs);
    LM_T(LmtMongo, ("Subscription attrs: %s", attrs.toString().c_str()));
  }
}
/* ****************************************************************************
*
* setEntities -
*/
static void setEntities(const SubscriptionUpdate& subUp, const BSONObj& subOrig, BSONObjBuilder* b)
{
  if (subUp.subjectProvided && !subUp.fromNgsiv1)
  {
    //
    // NGSIv1 doesn't allow to change entities,
    // see https://fiware-orion.readthedocs.io/en/master/user/updating_regs_and_subs/index.html
    //
    setEntities(subUp, b);
  }
  else
  {
    BSONArray entities = getArrayFieldF(subOrig, CSUB_ENTITIES);

    b->append(CSUB_ENTITIES, entities);
    LM_T(LmtMongo, ("Subscription entities: %s", entities.toString().c_str()));
  }
}
/* ****************************************************************************
*
* setMetadata -
*/
static void setMetadata(const SubscriptionUpdate& subUp, const BSONObj& subOrig, BSONObjBuilder* b)
{
  if (subUp.notificationProvided)
  {
    setMetadata(subUp, b);
  }
  else
  {
    //
    // Note that if subOrig doesn't have CSUB_METADATA (e.g. old subscription in the DB created before
    // this feature) BSONArray constructor ensures an empty array
    //
    BSONArray metadata;

    if (subOrig.hasField(CSUB_METADATA))
    {
      metadata = getArrayFieldF(subOrig, CSUB_METADATA);
    }

    b->append(CSUB_METADATA, metadata);
    LM_T(LmtMongo, ("Subscription metadata: %s", metadata.toString().c_str()));
  }
}
/* ****************************************************************************
*
* setCondsAndInitialNotify -
*/
static void setCondsAndInitialNotify
(
  const SubscriptionUpdate&        subUp,
  const BSONObj&                   subOrig,
  const std::string&               tenant,
  const std::vector<std::string>&  servicePathV,
  const std::string&               xauthToken,
  const std::string&               fiwareCorrelator,
  BSONObjBuilder*                  b,
  const bool&                      skipInitialNotification,
  bool*                            notificationDone
)
{
  // notification can be changed to true by setCondsAndInitialNotify()
  *notificationDone = false;

  if (subUp.subjectProvided)
  {
    std::string               status;
    HttpInfo                  httpInfo;
    bool                      blacklist;
    std::vector<std::string>  notifAttributesV;
    std::vector<std::string>  metadataV;
    RenderFormat              attrsFormat = NGSI_V2_NORMALIZED;

    if (subUp.statusProvided)
    {
      status = subUp.status;
    }
    else
    {
      status = subOrig.hasField(CSUB_STATUS)? getStringFieldF(subOrig, CSUB_STATUS) : STATUS_ACTIVE;
    }

    if (subUp.notificationProvided)
    {
      httpInfo         = subUp.notification.httpInfo;
      blacklist        = subUp.notification.blacklist;
      metadataV        = subUp.notification.metadata;
      notifAttributesV = subUp.notification.attributes;
    }
    else
    {
      httpInfo.fill(subOrig);
      blacklist = subOrig.hasField(CSUB_BLACKLIST)? getBoolFieldF(subOrig, CSUB_BLACKLIST) : false;
      setStringVectorF(subOrig, CSUB_ATTRS, &notifAttributesV);

      if (subOrig.hasField(CSUB_METADATA))
      {
        setStringVectorF(subOrig, CSUB_METADATA, &metadataV);
      }
    }

    if (subUp.attrsFormatProvided)
    {
      attrsFormat = subUp.attrsFormat;
    }
    else if (subOrig.hasField(CSUB_FORMAT))
    {
      attrsFormat = stringToRenderFormat(getStringFieldF(subOrig, CSUB_FORMAT));
    }

    if (subUp.fromNgsiv1)
    {
      //
      // In NGSIv1 is legal updating conditions without updating entities, which is not possible
      // in NGSIv2 (as both entities and coditions are part of 'subject' and they are updated as
      // a whole). In addition, NGSIv1 doesn't allow to update notification attributes. Both
      // (entities and notification attributes) are passed in subOrig.
      //
      // See: https://fiware-orion.readthedocs.io/en/master/user/updating_regs_and_subs/index.html
      //
      setCondsAndInitialNotifyNgsiv1(subUp,
                                     subOrig,
                                     subUp.id,
                                     status,
                                     httpInfo.url,
                                     attrsFormat,
                                     tenant,
                                     servicePathV,
                                     xauthToken,
                                     fiwareCorrelator,
                                     b,
                                     skipInitialNotification,
                                     notificationDone);
    }
    else
    {
      setCondsAndInitialNotify(subUp,
                               subUp.id,
                               status,
                               notifAttributesV,
                               metadataV,
                               httpInfo,
                               blacklist,
                               attrsFormat,
                               tenant,
                               servicePathV,
                               xauthToken,
                               fiwareCorrelator,
                               b,
                               notificationDone,
                               skipInitialNotification,
                               V2);
    }
  }
  else
  {
    BSONArray conds = getArrayFieldF(subOrig, CSUB_CONDITIONS);

    b->append(CSUB_CONDITIONS, conds);
    LM_T(LmtMongo, ("Subscription conditions: %s", conds.toString().c_str()));
  }
}
/* ****************************************************************************
*
* setCondsAndInitialNotifyNgsiv1 -
*
* This method could be removed along with the rest of NGSIv1 stuff
*/
static void setCondsAndInitialNotifyNgsiv1
(
  const Subscription&              sub,
  const BSONObj&                   subOrig,
  const std::string&               subId,
  const std::string&               status,
  const std::string&               url,
  RenderFormat                     attrsFormat,
  const std::string&               tenant,
  const std::vector<std::string>&  servicePathV,
  const std::string&               xauthToken,
  const std::string&               fiwareCorrelator,
  BSONObjBuilder*                  b,
  const bool&                      skipInitialNotification,
  bool*                            notificationDone
)
{
  //
  // Similar to setCondsAndInitialNotify() in MongoCommonSubscripion.cpp, but
  // entities and notifications attributes are not taken from sub but from subOrig
  //
  // Most of the following code is copied from mongoGetSubscription logic but, given
  // that this function is temporal, I don't worry too much about DRY-ness here

  std::vector<EntID>        entities;
  std::vector<BSONElement>  ents = getFieldF(subOrig, CSUB_ENTITIES).Array();

  for (unsigned int ix = 0; ix < ents.size(); ++ix)
  {
    BSONObj     ent       = ents[ix].embeddedObject();
    std::string id        = getStringFieldF(ent, CSUB_ENTITY_ID);
    std::string type      = ent.hasField(CSUB_ENTITY_TYPE)? getStringFieldF(ent, CSUB_ENTITY_TYPE) : "";
    std::string isPattern = getStringFieldF(ent, CSUB_ENTITY_ISPATTERN);
    EntID       en;

    if (isFalse(isPattern))
    {
      en.id = id;
    }
    else
    {
      en.idPattern = id;
    }
    en.type = type;

    entities.push_back(en);
  }

  std::vector<std::string> attributes;
  std::vector<std::string> metadata;

  setStringVectorF(subOrig, CSUB_ATTRS, &attributes);

  if (subOrig.hasField(CSUB_METADATA))
  {
    setStringVectorF(subOrig, CSUB_METADATA, &metadata);
  }

  /* Conds vector (and maybe an initial notification) */
  *notificationDone = false;

  BSONArray  conds = processConditionVector(sub.subject.condition.attributes,
                                            entities,
                                            attributes,
                                            metadata,
                                            subId,
                                            ngsiv2::HttpInfo(url),
                                            notificationDone,
                                            attrsFormat,
                                            tenant,
                                            xauthToken,
                                            servicePathV,
                                            &(sub.restriction),
                                            status,
                                            fiwareCorrelator,
                                            sub.notification.attributes,
                                            sub.notification.blacklist,
                                            skipInitialNotification,
                                            V1);

  b->append(CSUB_CONDITIONS, conds);
  LM_T(LmtMongo, ("Subscription conditions: %s", conds.toString().c_str()));
}