/* ****************************************************************************
*
* release - just exercise the code
*/
TEST(ContextAttributeResponse, release)
{
  ContextAttribute*         caP = new ContextAttribute("caName", "caType", "caValue");
  ContextAttributeResponse  car;

  utInit();

  car.contextAttributeVector.push_back(caP);
  car.statusCode.fill(SccOk);

  car.release();

  utExit();
}
/* ****************************************************************************
*
* present - just exercise the code
*/
TEST(ContextAttributeResponse, present)
{
  ContextAttribute          ca("caName", "caType", "caValue");
  ContextAttributeResponse  car;

  utInit();

  car.contextAttributeVector.push_back(&ca);
  car.statusCode.fill(SccOk);

  car.present("");

  utExit();
}
/* ****************************************************************************
*
* render_xml - 
*/
TEST(ContextAttributeResponse, render_xml)
{
  ContextAttribute          ca("caName", "caType", "caValue");
  ContextAttributeResponse  car;
  std::string               out;
  ConnectionInfo            ci;

  utInit();

  car.contextAttributeVector.push_back(&ca);
  car.statusCode.fill(SccOk);

  out = car.render(&ci, ContextEntityAttributes, "");

  utExit();
}
/* ****************************************************************************
*
* getIndividualContextEntityAttribute -
*/
std::string getIndividualContextEntityAttribute
(
  ConnectionInfo*            ciP,
  int                        components,
  std::vector<std::string>&  compV,
  ParseData*                 parseDataP
)
{
  std::string               answer;
  std::string               entityId      = compV[2];
  std::string               attributeName = compV[4];
  ContextAttributeResponse  response;

  LM_T(LmtConvenience, ("CONVENIENCE: got 'GET' request with %d components", components));

  ciP->httpStatusCode = mapGetIndividualContextEntityAttribute(entityId, "", attributeName, &response, ciP);
  answer = response.render(ciP, IndividualContextEntityAttribute, "");
  response.release();

  return answer;
}
/* ****************************************************************************
*
* check_json - 
*/
TEST(ContextAttributeResponse, check_json)
{
  ContextAttribute          ca("caName", "caType", "caValue");
  ContextAttributeResponse  car;
  std::string               out;
  const char*               outfile1 = "ngsi10.contextAttributeResponse.check3.valid.json";
  const char*               outfile2 = "ngsi10.contextAttributeResponse.check4.valid.json";
  ConnectionInfo            ci;

  utInit();

  // 1. OK
  car.contextAttributeVector.push_back(&ca);
  car.statusCode.fill(SccOk, "OK"); 

  ci.outFormat = JSON;
  out = car.check(&ci, UpdateContextAttribute, "", "", 0);
  EXPECT_STREQ("OK", out.c_str());


  // 2. predetectedError
  out = car.check(&ci, UpdateContextAttribute, "", "PRE Error", 0);
  EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), outfile1)) << "Error getting test data from '" << outfile1 << "'";
  EXPECT_STREQ(expectedBuf, out.c_str());


  // 3. Bad ContextAttribute
  ContextAttribute          ca2("", "caType", "caValue");
  car.contextAttributeVector.push_back(&ca2);
  
  LM_M(("car.contextAttributeVector.size: %d - calling ContextAttributeResponse::check", car.contextAttributeVector.size()));
  out = car.check(&ci, UpdateContextAttribute, "", "", 0);
  EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), outfile2)) << "Error getting test data from '" << outfile2 << "'";
  EXPECT_STREQ(expectedBuf, out.c_str());

  utExit();
}
/* ****************************************************************************
*
* getIndividualContextEntityAttributeWithTypeAndId -
*
* GET /v1/contextEntities/type/{entity::type}/id/{entity::id}/attributes/{attribute::name}
*
* Payload In:  None
* Payload Out: ContextAttributeResponse
*
* URI parameters:
*   - attributesFormat=object
*   - entity::type=XXX     (must coincide with entity::type in URL)
*   - !exist=entity::type  (if set - error -- entity::type cannot be empty)
*   - exist=entity::type   (not supported - ok if present, ok if not present ...)
*
* 01. Get values from URL (entityId::type, esist, !exist)
* 02. Check validity of URI params
* 03. Fill in QueryContextRequest
* 04. Call standard operation postQueryContext
* 05. If 404 Not Found - enter request info into response context element
* 06. Translate QueryContextResponse to ContextElementResponse
* 07. Cleanup and return result
*/
std::string getIndividualContextEntityAttributeWithTypeAndId
(
  ConnectionInfo*            ciP,
  int                        components,
  std::vector<std::string>&  compV,
  ParseData*                 parseDataP
)
{
  std::string               answer;
  std::string               entityType              = compV[3];
  std::string               entityId                = compV[5];
  std::string               attributeName           = compV[7];
  std::string               entityTypeFromUriParam  = ciP->uriParam[URI_PARAM_ENTITY_TYPE];
  EntityTypeInfo            typeInfo                = EntityTypeEmptyOrNotEmpty;
  ContextAttributeResponse  response;


  // 01. Get values from URL (entityId::type, esist, !exist)
  if (ciP->uriParam[URI_PARAM_NOT_EXIST] == URI_PARAM_ENTITY_TYPE)
  {
    typeInfo = EntityTypeEmpty;
  }
  else if (ciP->uriParam[URI_PARAM_EXIST] == URI_PARAM_ENTITY_TYPE)
  {
    typeInfo = EntityTypeNotEmpty;
  }


  //
  // 02. Check validity of URI params ...
  //     and if OK;
  // 03. Fill in QueryContextRequest
  // 04. Call standard operation postQueryContext
  // 05. If 404 Not Found - enter request info into response context element
  //
  if (typeInfo == EntityTypeEmpty)
  {
    parseDataP->qcrs.res.errorCode.fill(SccBadRequest, "entity::type cannot be empty for this request");
    LM_W(("Bad Input (entity::type cannot be empty for this request)"));
  }
  else if ((entityTypeFromUriParam != entityType) && (entityTypeFromUriParam != ""))
  {
    parseDataP->qcrs.res.errorCode.fill(SccBadRequest, "non-matching entity::types in URL");
    LM_W(("Bad Input non-matching entity::types in URL"));
  }
  else
  {
    // 03. Fill in QueryContextRequest
    parseDataP->qcr.res.fill(entityId, entityType, attributeName);


    // 04. Call standard operation postQueryContext
    parseDataP->qcrs.res.errorCode.fill(SccOk);
    postQueryContext(ciP, components, compV, parseDataP);

    // 05. If 404 Not Found - enter request info into response context element
    if (parseDataP->qcrs.res.errorCode.code == SccContextElementNotFound)
    {
      response.statusCode.details = "entityId::type/attribute::name pair not found";
    }
  }


  // 06. Translate QueryContextResponse to ContextAttributeResponse
  response.fill(&parseDataP->qcrs.res, entityId, entityType, attributeName, "");


  // 07. Cleanup and return result
  TIMED_RENDER(answer = response.render(ciP, RtContextAttributeResponse, ""));

  parseDataP->qcr.res.release();
  parseDataP->qcrs.res.release();
  response.release();

  return answer;
}
/* ****************************************************************************
*
* getAttributeValueInstance - 
*
* GET /ngsi10/contextEntities/{entityID}/attributes/{attributeName}/{valueID}
*/
std::string getAttributeValueInstance
(
  ConnectionInfo*            ciP,
  int                        components,
  std::vector<std::string>&  compV,
  ParseData*                 parseDataP
)
{
  QueryContextRequest      request;
  QueryContextResponse     response;
  std::string              entityId      = compV[2];
  std::string              attributeName = compV[4];
  std::string              valueID       = compV[5];
  EntityId*                eP            = new EntityId(entityId, "", "false");
  StatusCode               sc;
  ContextAttributeResponse car;

  request.entityIdVector.push_back(eP);
  request.attributeList.push_back(attributeName);

  ciP->httpStatusCode = mongoQueryContext(&request, &response, ciP->tenant, ciP->servicePathV, ciP->uriParam);

  if (response.contextElementResponseVector.size() == 0)
  {
     car.statusCode.fill(SccContextElementNotFound,
                         std::string("Entity-Attribute pair: /") + entityId + "-" + attributeName + "/");
  }
  else
  {
    ContextElementResponse* cerP = response.contextElementResponseVector.get(0);
    ContextAttributeVector cav = cerP->contextElement.contextAttributeVector;

    // FIXME P4: as long as mongoQueryContext() signature is based on NGSI standard operations and that
    // standard queryContext doesn't allow specify metadata for attributes (note that it uses xs:string,
    // not full fledge attribute types), we cannot pass the ID to mongoBackend so we need to do the for loop
    // to grep the right attribute among all the ones returned by mongoQueryContext. However, this involves
    // a suboptimal query at mongoBackend, which could be improved passing it the ID as a new parameter to
    // mongoQueryContext() (although breaking the design principle about mongo*() functions follow the NGSI
    // standard). To think about it.
    for (unsigned int i = 0; i < cav.size(); i++)
    {
      if (cav.get(i)->getId() == valueID)
      {
        car.contextAttributeVector.push_back(cav.get(i));
      }
    }

    if (cav.size() > 0 && car.contextAttributeVector.size() == 0)
    {
      car.statusCode.fill(SccContextElementNotFound,
                          std::string("Attribute-ValueID pair: /") + attributeName + "-" + valueID + "/");
    }
    else
    {
      car.statusCode.fill(&cerP->statusCode);
    }
  }

  request.release();

  return car.render(ciP, AttributeValueInstance, "");
}