/**
 * @brief Copy a operation into a state owner
 *
 * This is only used for NFSv4.0 and only for a specific subset of
 * operations for which it guarantees At-Most Once Semantics.
 *
 * @param[in,out] owner The owner to hold the operation
 * @param[in]     seqid Seqid of this operation
 * @param[in]     args  Arguments of operation to copy
 * @param[in]     data  Compound data
 * @param[in]     resp  Response to copy
 * @param[in]     tag   Arbitrary string for logging/debugging
 */
void Copy_nfs4_state_req(state_owner_t *owner,
                         seqid4 seqid,
                         nfs_argop4 *args,
                         cache_entry_t *entry,
                         nfs_resop4 *resp,
                         const char *tag)
{
  /* Simplify use of this function when we may not be keeping any data for the
   * state owner
   */
  if(owner == NULL)
    return;

  LogFullDebug(COMPONENT_STATE,
               "%s: saving response %p so_seqid %u new seqid %u",
               tag, owner, owner->so_owner.so_nfs4_owner.so_seqid, seqid);

  /* Free previous response */
  nfs4_Compound_FreeOne(&owner->so_owner.so_nfs4_owner.so_resp);

  /* Copy new response */
  nfs4_Compound_CopyResOne(&owner->so_owner.so_nfs4_owner.so_resp, resp);

  /* Deep copy OPEN args? */
  if(owner->so_owner.so_nfs4_owner.so_args.argop == NFS4_OP_OPEN)
    {
    }

  /* Copy bnew args */
  memcpy(&owner->so_owner.so_nfs4_owner.so_args,
         args,
         sizeof(owner->so_owner.so_nfs4_owner.so_args));

  /* Copy new file, note we don't take any reference, so this entry
   * might not remain valid, but the pointer value suffices here.
   */
  owner->so_owner.so_nfs4_owner.so_last_entry = entry;

  /* Deep copy OPEN args? */
  if(args->argop == NFS4_OP_OPEN)
    {
    }

  /* Store new seqid */
  owner->so_owner.so_nfs4_owner.so_seqid = seqid;
}
void Copy_nfs4_state_req(state_owner_t   * powner,
                         seqid4            seqid,
                         nfs_argop4      * args,
                         compound_data_t * data,
                         nfs_resop4      * resp,
                         const char      * tag)
{
  /* Simplify use of this function when we may not be keeping any data for the
   * state owner
   */
  if(powner == NULL)
    return;

  LogFullDebug(COMPONENT_STATE,
               "%s: saving response %p so_seqid %u new seqid %u",
               tag, powner, powner->so_owner.so_nfs4_owner.so_seqid, seqid);

  /* Free previous response */
  nfs4_Compound_FreeOne(&powner->so_owner.so_nfs4_owner.so_resp);

  /* Copy new response */
  nfs4_Compound_CopyResOne(&powner->so_owner.so_nfs4_owner.so_resp, resp);

  /* Deep copy OPEN args? */
  if(powner->so_owner.so_nfs4_owner.so_args.argop == NFS4_OP_OPEN)
    {
    }

  /* Copy bnew args */
  memcpy(&powner->so_owner.so_nfs4_owner.so_args,
         args,
         sizeof(powner->so_owner.so_nfs4_owner.so_args));

  /* Copy new file */
  powner->so_owner.so_nfs4_owner.so_last_pentry = data->current_entry;

  /* Deep copy OPEN args? */
  if(args->argop == NFS4_OP_OPEN)
    {
    }

  /* Store new seqid */
  powner->so_owner.so_nfs4_owner.so_seqid = seqid;
}
/**
 *
 * Check_nfs4_seqid: Check NFS4 request for valid seqid for replay, next request, or BAD_SEQID.
 *
 * Returns TRUE if the request is the next seqid.
 * If the request is a replay, copies the saved response and returns FALSE.
 * Otherwise, sets status to NFS4ERR_BAD_SEQID and returns FALSE.
 *
 * In either case, on a FALSE return, the caller should send the resulting response back to the client.
 *
 */
bool_t Check_nfs4_seqid(state_owner_t   * powner,
                        seqid4            seqid,
                        nfs_argop4      * args,
                        compound_data_t * data,
                        nfs_resop4      * resp,
                        const char      * tag)
{
  seqid4 next;

  /* Check if any owner to verify seqid against */
  if(powner == NULL)
    {
      LogFullDebug(COMPONENT_STATE,
               "%s: Unknown owner doesn't have saved seqid, req seqid %u",
               tag, seqid);
      return TRUE;
    }

  /* If this is a new state owner, client may start with any seqid */
  if(powner->so_owner.so_nfs4_owner.so_last_pentry == NULL)
    {
      LogFullDebug(COMPONENT_STATE,
               "%s: New owner %p doesn't have saved seqid, req seqid %u",
               tag, powner, seqid);
      return TRUE;
    }

  /* Check for valid next seqid */
  next = powner->so_owner.so_nfs4_owner.so_seqid + 1;

  LogFullDebug(COMPONENT_STATE,
               "%s: Check powner %p so_seqid %u next %u req seqid %u",
               tag, powner, powner->so_owner.so_nfs4_owner.so_seqid, next, seqid);

  if(seqid == next)
    return TRUE;

  /* All NFS4 responses have the status in the same place, so use any to set NFS4ERR_BAD_SEQID */
  resp->nfs_resop4_u.oplock.status = NFS4ERR_BAD_SEQID;

  /* Now check for valid replay */
  if(powner->so_owner.so_nfs4_owner.so_seqid != seqid)
    {
      LogDebug(COMPONENT_STATE,
               "%s: Invalid seqid %u in request (not replay), expected seqid %u, returning NFS4ERR_BAD_SEQID",
               tag, seqid, powner->so_owner.so_nfs4_owner.so_seqid);
      return FALSE;
    }

  if(args->argop != powner->so_owner.so_nfs4_owner.so_args.argop)
    {
      LogDebug(COMPONENT_STATE,
               "%s: Invalid seqid in request %u (not replay - not same op), returning NFS4ERR_BAD_SEQID",
               tag, seqid);
      return FALSE;
    }

  if(powner->so_owner.so_nfs4_owner.so_last_pentry != data->current_entry)
    {
      LogDebug(COMPONENT_STATE,
               "%s: Invalid seqid in request %u (not replay - wrong file), returning NFS4ERR_BAD_SEQID",
               tag, seqid);
      return FALSE;
    }

  // TODO FSF: add more checks here...

  LogDebug(COMPONENT_STATE,
           "%s: Copying saved response for seqid %u",
           tag, seqid);

  /* Copy the saved response and tell caller to use it */
  nfs4_Compound_CopyResOne(resp, &powner->so_owner.so_nfs4_owner.so_resp);

  return FALSE;
}
/**
 * @brief Check NFS4 request for valid seqid for replay, next request, or BAD_SEQID.
 *
 * Returns true if the request is the next seqid.  If the request is a
 * replay, copies the saved response and returns false.  Otherwise,
 * sets status to NFS4ERR_BAD_SEQID and returns false.
 *
 * In either case, on a false return, the caller should send the
 * resulting response back to the client.
 *
 * @param[in]  owner Owner to check
 * @param[in]  seqid Seqid to check
 * @param[in]  args  Arguments of operation
 * @param[in]  data  Compound data
 * @param[out] resp  Cached request, if replay
 * @param[in]  tag   Arbitrary string for logging/debugging
 *
 * @retval true if the caller should process the operation.
 * @retval false if the caller should immediately return the provides response.
 */
bool Check_nfs4_seqid(state_owner_t *owner, seqid4 seqid, nfs_argop4 *args,
		      cache_entry_t *entry, nfs_resop4 *resp, const char *tag)
{
	seqid4 next;
	char str[LOG_BUFF_LEN];
	struct display_buffer dspbuf = {sizeof(str), str, str};
	bool str_valid = false;

	/* Check if any owner to verify seqid against */
	if (owner == NULL) {
		LogFullDebug(COMPONENT_STATE,
			     "%s: Unknown owner doesn't have saved seqid, req seqid %u",
			     tag, seqid);
		return true;
	}

	if (isDebug(COMPONENT_STATE)) {
		display_owner(&dspbuf, owner);
		str_valid = true;
	}

	/* If this is a new state owner, client may start with any seqid */
	if (owner->so_owner.so_nfs4_owner.so_last_entry == NULL) {
		if (str_valid)
			LogFullDebug(COMPONENT_STATE,
				     "%s: New {%s} doesn't have saved seqid, req seqid %u",
				     tag, str, seqid);
		return true;
	}

	/* Check for valid next seqid */
	next = owner->so_owner.so_nfs4_owner.so_seqid + 1;

	if (str_valid)
		LogFullDebug(COMPONENT_STATE,
			     "%s: Check {%s} next %u req seqid %u",
			     tag, str, next, seqid);

	if (seqid == next)
		return true;

	/* All NFS4 responses have the status in the same place, so use any to
	 * set NFS4ERR_BAD_SEQID
	 */
	resp->nfs_resop4_u.oplock.status = NFS4ERR_BAD_SEQID;

	/* Now check for valid replay */
	if (owner->so_owner.so_nfs4_owner.so_seqid != seqid) {
		if (str_valid)
			LogDebug(COMPONENT_STATE,
				 "%s: Invalid seqid %u in request (not replay), expected seqid for {%s}, returning NFS4ERR_BAD_SEQID",
				 tag, seqid, str);
		return false;
	}

	if (args->argop != owner->so_owner.so_nfs4_owner.so_args.argop) {
		if (str_valid)
			LogDebug(COMPONENT_STATE,
				 "%s: Invalid seqid %u in request (not replay - not same op), expected seqid for {%s}, returning NFS4ERR_BAD_SEQID",
				 tag, seqid, str);
		return false;
	}

	if (owner->so_owner.so_nfs4_owner.so_last_entry != entry) {
		if (str_valid)
			LogDebug(COMPONENT_STATE,
				 "%s: Invalid seqid %u in request (not replay - wrong file), expected seqid for {%s}, returning NFS4ERR_BAD_SEQID",
				 tag, seqid, str);
		return false;
	}

	/** @todo FSF: add more checks here... */

	if (str_valid)
		LogDebug(COMPONENT_STATE,
			 "%s: Copying saved response for seqid %u into {%s}",
			 tag, seqid, str);

	/* Copy the saved response and tell caller to use it */
	nfs4_Compound_CopyResOne(resp, &owner->so_owner.so_nfs4_owner.so_resp);

	return false;
}