/// True/False, based on whether OTNumLists MATCH in COUNT and CONTENT (NOT ORDER.)
///
bool OTNumList::Verify(const OTNumList & rhs) const
{
    // Verify they have the same number of elements.
    //
    if (this->Count() != rhs.Count())
        return false;
    // -------------------

    // Verify each value on *this is also found on rhs.
    //
    FOR_EACH(std::set<int64_t>, m_setData)
    {
        const int64_t lValue = *it;
        // ----------
        if (false == rhs.Verify(lValue))
            return false;
    }

    return true;
}
void OTTransactionType::GetNumList(OTNumList & theOutput)
{    
    theOutput.Release();
    theOutput.Add(m_Numlist);
}
void OTTrackable::GetAllTransactionNumbers(OTNumList & numlistOutput) const
{
    if (m_lTransactionNum > 0)
        numlistOutput.Add(m_lTransactionNum);
}
// OTMessageOutbuffer deletes the OTMessage when you call this.
//
bool OTMessageOutbuffer::RemoveSentMessage(const int64_t & lRequestNum, const OTString & strServerID, const OTString & strNymID)
{
    OTString strFolder, strFile;
    strFolder.Format("%s%s%s%s%s%s%s",
                     OTFolders::Nym().Get(),         OTLog::PathSeparator(),
                     strServerID.Get(),          OTLog::PathSeparator(),
                     "sent", /*todo hardcoding*/ OTLog::PathSeparator(),
                     strNymID.Get());
    strFile.Format("%lld.msg", lRequestNum);
    // ------------------------------------------------
    mapOfMessages::iterator it = m_mapMessages.begin();
    
    bool bReturnValue = false;
    
    while (it != m_mapMessages.end())
    {
        // -----------------------------
        const int64_t  & lTempReqNum   = it->first;
        // -----------------------
        if (lTempReqNum != lRequestNum)
        {
            ++it;
            continue;
        }
        // -----------------------
        OTMessage   * pMsg          = it->second;
        OT_ASSERT(NULL != pMsg);
        // -----------------------------
        //
        // If a server ID was passed in, but doesn't match the server ID on this message,
        // Then skip this one. (Same with the NymID.)
        if (!strServerID.Compare(pMsg->m_strServerID) ||
            !strNymID.   Compare(pMsg->m_strNymID))
        {
            ++it;
            continue;
        }
        // --------
        else
        {
            delete pMsg; pMsg = NULL;
            // ----------------------
            mapOfMessages::iterator temp_it = it;
            ++temp_it;
            m_mapMessages.erase(it);
            it = temp_it; // here's where it gets incremented. (During the erase, basically.)
            // ----------------------
            bReturnValue = true;
            break;
        }
    }
    // ----------------------------------
    // Whether we found it in RAM or not, let's make sure to delete it from
    // local storage, if it's there... (Since there's a list there we have to update,
    // anyway.)
    // We keep a list of the request numbers, so let's load it up, remove the number
    // from that list, and then save it again.
    // ----------------------------------
    OTNumList theNumList;
    std::string str_data_filename("sent.dat");  // todo hardcoding.
    if (OTDB::Exists(strFolder.Get(), str_data_filename))
    {
        OTString strNumList(OTDB::QueryPlainString(strFolder.Get(), str_data_filename));
        if (strNumList.Exists())
            theNumList.Add(strNumList);
        theNumList.Remove(lRequestNum);
    }
    else // it doesn't exist on disk, so let's just create it from the list we have in RAM so we can store it to disk.
    {
        it = m_mapMessages.begin();
        while (it != m_mapMessages.end())
        {
            // -----------------------------
            const int64_t  & lTempReqNum   = it->first;
            // -----------------------
            OTMessage   * pMsg          = it->second;
            OT_ASSERT(NULL != pMsg);
            // -----------------------------
            //
            // If a server ID was passed in, but doesn't match the server ID on this message,
            // Then skip this one. (Same with the NymID.)
            //
            if (!strServerID.Compare(pMsg->m_strServerID) ||
                !strNymID.   Compare(pMsg->m_strNymID))
            {
                ++it;
                continue;
            }
            // --------
            else
            {
                theNumList.Add(lTempReqNum);
            }
            ++it;
        }
    }// else
    // ----------------------------------
    // By this point, theNumList has either been loaded from local storage and had the number removed,
    // or it wasn't in local storage and thus we created it and added all the numbers to it from RAM (not
    // including the one being erased, since it was already removed from the RAM list, above.) So either
    // way, the number being removed is now ABSENT from theNumList.
    //
    // Therefore nothing left to do here, but save it back again!
    //
    OTString strOutput;
    theNumList.Output(strOutput);
    if (!OTDB::StorePlainString(strOutput.Get(), strFolder.Get(), str_data_filename))
    {
        OTLog::Error("OTMessageOutbuffer::RemoveSentMessage: Error: failed writing list of request numbers to storage.\n");
    }
    // ----------------------------------
    // Now that we've updated the numlist in local storage, let's
    // erase the sent message itself...
    //
    OTMessage * pMsg = new OTMessage;
    OT_ASSERT(NULL != pMsg);
    OTCleanup<OTMessage> theMsgAngel(pMsg);
    
    if (OTDB::Exists(strFolder.Get(), strFile.Get()) && pMsg->LoadContract(strFolder.Get(), strFile.Get()))
    {
        OTDB::EraseValueByKey(strFolder.Get(), strFile.Get());
        return true;
    }
    // ----------------------------------
	return bReturnValue;
}
// WARNING: ONLY call this (with arguments) directly after a successful @getNymbox has been received!
// See comments below for more details.
//
void OTMessageOutbuffer::Clear(const OTString * pstrServerID/*=NULL*/, const OTString * pstrNymID/*=NULL*/, OTPseudonym * pNym/*=NULL*/,
                               const bool     * pbHarvestingForRetry/*=NULL*/)
{
//  const char * szFuncName		= "OTMessageOutbuffer::Clear";
    // -----------------------------------------------
    
    mapOfMessages::iterator it = m_mapMessages.begin();
    
    while (it != m_mapMessages.end())
    {
        // -----------------------------
        const int64_t  & lRequestNum   = it->first;
        OTMessage   * pThisMsg      = it->second;
        OT_ASSERT(NULL != pThisMsg);
        // -----------------------------
        //
        // If a server ID was passed in, but doesn't match the server ID on this message,
        // Then skip this one. (Same with the NymID.)
        if (
            ((NULL != pstrServerID) && !pstrServerID->Compare(pThisMsg->m_strServerID)) ||
            ((NULL != pstrNymID)    && !pstrNymID->Compare   (pThisMsg->m_strNymID))
            )
        {
            ++it;
            continue;
        }
        // --------
        else
        {
            /*
             Sent messages are cached because some of them are so important, that
             the server drops a reply notice into the Nymbox to make sure they were
             received. This way, when we download the Nymbox we can SEE which messages
             were ACTUALLY replied to, and at that time, we removed those messages
             already from *this "sent buffer." After that loop was done, we called
             CLEAR (this function) and cleared ALL the sent messages from the buffer
             (for the appropriate server and nym IDs...clear without those IDs is
             only for the destructor.)
             
             This Clear, where we are now, HARVESTS the transaction numbers back
             from any messages left in the sent buffer. We are able to do this with
             confidence because we know that this function is only called in @getNymbox
             on client side, and only after the ones with actual replies (as evidenced
             by the Nymbox) have already been removed from *this "sent buffer."
             
             Why were they removed in advance? Because clearly: if the server HAS replied
             to them already, then there's no need to harvest anything: just let it
             process as normal, whether the transaction inside is a success or fail.
             (We KNOW the message didn't fail because otherwise there wouldn't even be
             a notice in the Nymbox. So this is about the transaction inside.)

             So we remove the ones that we DEFINITELY know the server HAS replied to.
             
             And the ones remaining? We know for those, the server definitely has NOT
             replied to them (the message must have been dropped by the network or
             something.) How do we know this? Because there would be a notice in the
             Nymbox! So at the moment of successful @getNymbox, we are able to loop through
             those receipts and know FOR SURE, WHICH ones definitely have a reply, and
             which ones definitely DO NOT.
             
             The ones where we definitely do NOT have a reply--that is, the ones that are in
             the "sent messages" buffer, but are not in the Nymbox with the same request
             number--we harvest those numbers, since the server clearly never saw them, or
             rejected the message before the transaction itself even had a chance to run.
             
             */
            if (NULL != pNym)
            {
                OT_ASSERT(NULL != pstrNymID && pstrNymID->Exists());
                const OTIdentifier MSG_NYM_ID(*pstrNymID);
                OT_ASSERT(pNym->CompareID(MSG_NYM_ID));
                // ----------------------------
                OT_ASSERT(NULL != pstrServerID && pstrServerID->Exists());
                // ----------------------------
                OT_ASSERT(NULL != pbHarvestingForRetry);
                // ----------------------------
                /*
                 getNymbox			-- client is NOT sending hash, server is NOT rejecting bad hashes, server IS SENDING HASH in the @getNymbox reply
                 getRequest			-- client is NOT sending hash, server is NOT rejecting bad hashes, server IS SENDING HASH in the @getRequest reply
                 
                 processNymbox		-- client is SENDING HASH, server is REJECTING BAD HASHES, server is SENDING HASH in the @processNymbox  reply
                 notarizeTransactions	-- client is SENDING HASH, server is REJECTING BAD HASHES, server is SENDING HASH in the @notarizeTransactions  reply
                 processInbox 		-- client is SENDING HASH, server is REJECTING BAD HASHES, server is SENDING HASH in the @processInbox  reply
                 triggerClause 		-- client is SENDING HASH, server is REJECTING BAD HASHES, server is SENDING HASH in the @triggerClause reply
                 
                 getTransactionNum 	-- client is SENDING HASH, server is REJECTING BAD HASHES, server is SENDING HASH in the @getTransactionNum reply
                 
                 Already covered in NotarizeTransaction: 
                    transfer, withdrawal, deposit, marketOffer, paymentPlan, smartContract, cancelCronItem, exchangeBasket
                 */
                
                if (pThisMsg->m_ascPayload.Exists() &&
                    (
                     pThisMsg->m_strCommand.Compare("processNymbox")        ||
                     pThisMsg->m_strCommand.Compare("processInbox")         ||
                     pThisMsg->m_strCommand.Compare("notarizeTransactions") ||
                     pThisMsg->m_strCommand.Compare("triggerClause")
                    )
                   )
                {
                    // 
                    // If we are here in the first place (i.e. after @getNymbox just removed
                    // all the messages in this sent buffer that already had a reply sitting
                    // in the nymbox) therefore we KNOW any messages in here never got a reply
                    // from the server, 
                    
                    const bool bReplyWasSuccess        = false; // If the msg had been a success, the reply (whether transaction within succeeded or failed) would have been dropped into my Nymbox, and thus removed from this "sent buffer" in @getNymbox.
                    const bool bReplyWasFailure        = true; // If the msg had been an explicit failure, the reply (without the transaction inside of it even having a chance to succeed or fail) would definitely NOT have been dropped into my Nymbox, and thus removed from this "sent buffer" in @getNymbox. However, IN THIS ONE CASE, since we DID just download the Nymbox and verify there ARE NO REPLIES for this request number (before calling this function), and since a dropped message is basically identical to a rejected message, since in either case, the transaction itself never even had a chance to run, we are able to now harvest the message AS IF the server HAD explicitly rejected the message. This is why I pass true here, where anywhere else in the code I would always pass false unless I had explicitly received a failure from the server. This place in the code, where we are now, is the failsafe endpoint for missed/dropped messages! IF they STILL haven't been found by this point, they are cleaned up as if the message was explicitly rejected by the server before the transaction even had a chance to run.

                    const bool bTransactionWasSuccess  = false; // Per above, since "the transaction never had a chance to run" then it could NOT have been an explicit success.
                    const bool bTransactionWasFailure  = false; // Per above, since "the transaction never had a chance to run" then it could NOT have been an explicit failure.
                    // -----------------------------------------------------
                    pThisMsg->HarvestTransactionNumbers(*pNym,      // Actually it's pNym who is "harvesting" the numbers in this call.   <========= HARVEST
                                                        *pbHarvestingForRetry,
                                                        bReplyWasSuccess,
                                                        bReplyWasFailure,
                                                        bTransactionWasSuccess,
                                                        bTransactionWasFailure);
                } // if there's a transaction to be harvested inside this message.
            } // if pNym !NULL
            // ----------------------
            mapOfMessages::iterator temp_it = it;
            ++temp_it;
            m_mapMessages.erase(it);
            it = temp_it; // here's where the iterator gets incremented (during the erase, basically.)
            // ----------------------
            delete pThisMsg;                // <============ DELETE
            pThisMsg = NULL;
            // ---------------------------------------------------------------------------
            if (NULL != pstrNymID && NULL != pstrServerID)
            {
                OTString strFolder, strFile;
                strFolder.Format("%s%s%s%s%s%s%s",
                                 OTFolders::Nym().Get(),     OTLog::PathSeparator(),
                                 pstrServerID->Get(),        OTLog::PathSeparator(),
                                 "sent", /*todo hardcoding*/ OTLog::PathSeparator(),
                                 pstrNymID->Get());
                strFile.Format("%lld.msg", lRequestNum);
                // ---------------------------------------------------------------------------
                OTNumList theNumList;
                std::string str_data_filename("sent.dat");  // todo hardcoding.
                if (OTDB::Exists(strFolder.Get(), str_data_filename))
                {
                    OTString strNumList(OTDB::QueryPlainString(strFolder.Get(), str_data_filename));
                    if (strNumList.Exists())
                        theNumList.Add(strNumList);
                    theNumList.Remove(lRequestNum); // Clear (this function) loops and removes them. (Here's the one being removed this iteration.)
                }
                else // it doesn't exist on disk, so let's just create it from the list we have in RAM so we can store it to disk.
                {    // NOTE: this may be unnecessary since we are "clear"ing them all anyway. But that just means we can remove this
                     // block during optimization. Todo optimize.
                     // Since we create the NumList based on m_mapMessages, and since the message for this iteration was already removed
                     // above, we don't need to remove anything at this point, we just create the NumList to contain the same numbers as are
                     // in m_mapMessages.
                     //
                    it = m_mapMessages.begin();
                    while (it != m_mapMessages.end())
                    {
                        // -----------------------------
                        const int64_t  & lTempReqNum   = it->first;
                        // -----------------------
                        OTMessage   * pMsg          = it->second;
                        OT_ASSERT(NULL != pMsg);
                        // -----------------------------
                        //
                        // If a server ID was passed in, but doesn't match the server ID on this message,
                        // Then skip this one. (Same with the NymID.)
                        //
                        if (!pstrServerID->Compare(pMsg->m_strServerID) ||
                            !pstrNymID->   Compare(pMsg->m_strNymID))
                        {
                            ++it;
                            continue;
                        }
                        // --------
                        else
                        {
                            theNumList.Add(lTempReqNum);
                        }
                        ++it;
                    }
                }// else
                // ----------------------------------
                // By this point, theNumList has either been loaded from local storage and had the number removed,
                // or it wasn't in local storage and thus we created it and added all the numbers to it from RAM (not
                // including the one being erased, since it was already removed from the RAM list, above.) So either
                // way, the number being removed is now ABSENT from theNumList.
                //
                // Therefore nothing left to do here, but save it back again!
                //
                OTString strOutput;
                theNumList.Output(strOutput);
                if (!OTDB::StorePlainString(strOutput.Get(), strFolder.Get(), str_data_filename)) // todo hardcoding.
                {
                    OTLog::Error("OTMessageOutbuffer::Clear: Error: failed writing list of request numbers to storage.\n");
                }
                // ----------------------------------
                // Make sure any messages being erased here, are also erased from local storage.
                // Now that we've updated the numlist in local storage, let's
                // erase the sent message itself...
                //
                OTMessage * pMsg = new OTMessage;
                OT_ASSERT(NULL != pMsg);
                OTCleanup<OTMessage> theMsgAngel(pMsg);
                
                if (OTDB::Exists(strFolder.Get(), strFile.Get()) && pMsg->LoadContract(strFolder.Get(), strFile.Get()))
                {
                    OTDB::EraseValueByKey(strFolder.Get(), strFile.Get());
                }
            }
            // ---------------------------------------------------------------------------
        }
    }
}
OTMessage * OTMessageOutbuffer::GetSentMessage(const int64_t & lRequestNum, const OTString & strServerID, const OTString & strNymID)
{
    mapOfMessages::iterator it = m_mapMessages.begin();
    
    for ( ; it != m_mapMessages.end(); ++it)
    {
        // -----------------------------
        const int64_t  & lTempReqNum   = it->first;
        // -----------------------
        if (lTempReqNum != lRequestNum)
        {
            continue;
        }
        // -----------------------
        OTMessage   * pMsg          = it->second;
        OT_ASSERT(NULL != pMsg);
        // -----------------------------
        //
        // If a server ID was passed in, but doesn't match the server ID on this message,
        // Then skip this one. (Same with the NymID.)
        if (!strServerID.Compare(pMsg->m_strServerID) ||
            !strNymID.   Compare(pMsg->m_strNymID))
        {
            continue;
        }
        // --------
        else
        {
            return pMsg;
        }
    }
    // ----------------------------------
    // Didn't find it? Okay let's load it from local storage, if it's there...
    //
    OTString strFolder, strFile;
    strFolder.Format("%s%s%s%s%s%s%s",
                     OTFolders::Nym().Get(),         OTLog::PathSeparator(),
                     strServerID.Get(),          OTLog::PathSeparator(),
                     "sent", /*todo hardcoding*/ OTLog::PathSeparator(),
                     strNymID.Get());
    strFile.Format("%lld.msg", lRequestNum);
    // -----------------------------------
    // Check the existing list, if it exists.
    //
    OTNumList theNumList;
    std::string str_data_filename("sent.dat");
    if (OTDB::Exists(strFolder.Get(), str_data_filename)) // todo hardcoding.
    {
        OTString strNumList(OTDB::QueryPlainString(strFolder.Get(), str_data_filename));
        
        if (strNumList.Exists())
            theNumList.Add(strNumList);
        
        if (theNumList.Verify(lRequestNum))
        {
            // Even if the outgoing message was stored, we still act like it
            // "doesn't exist" if it doesn't appear on the official list.
            // The list is what matters -- the message is just the contents referenced
            // by that list.
            // -----------------------------------
            OTMessage * pMsg = new OTMessage;
            OT_ASSERT(NULL != pMsg);
            OTCleanup<OTMessage> theMsgAngel(pMsg);
            
            if (OTDB::Exists(strFolder.Get(), strFile.Get()) && pMsg->LoadContract(strFolder.Get(), strFile.Get()))
            {
                // Since we had to load it from local storage, let's add it to
                // the list in RAM.
                //
                m_mapMessages.insert(std::pair<int64_t, OTMessage *>(lRequestNum, pMsg));
                theMsgAngel.SetCleanupTargetPointer(NULL);
                return pMsg;
            }
            // ----------------------------------
        }
    }
    // ----------------------------------
    // STILL didn't find it? (Failure.)
    //
	return NULL;
}
void OTMessageOutbuffer::AddSentMessage(OTMessage & theMessage) // must be heap allocated.
{
    int64_t lRequestNum = 0;
    
    if (theMessage.m_strRequestNum.Exists())
        lRequestNum = atol(theMessage.m_strRequestNum.Get()); // The map index is the request number on the message itself.
    // ----------------
    // It's technically possible to have TWO messages (from two different
    // servers) that happen to have the same request number. So we verify
    // that here, before removing any old ones with the same number and IDs.
    //
    mapOfMessages::iterator it = m_mapMessages.begin();
    
    for (; it != m_mapMessages.end(); ++it)
    {
        // -----------------------------
        const int64_t  & lTempReqNum   = it->first;
        // -----------------------
        if (lTempReqNum != lRequestNum)
        {
            continue;
        }
        // -----------------------
        OTMessage   * pMsg          = it->second;
        OT_ASSERT(NULL != pMsg);
        // -----------------------------
        //
        // If a server ID was passed in, but doesn't match the server ID on this message,
        // Then skip this one. (Same with the NymID.)
        //
        if (!theMessage.m_strServerID.Compare(pMsg->m_strServerID) ||
            !theMessage.m_strNymID.   Compare(pMsg->m_strNymID))
        {
            continue;
        }
        // --------
        else
        {
            delete pMsg;
            pMsg = NULL;
            m_mapMessages.erase(it);
            break;
        }
    }
    // Whatever it was, it's gone now!
    // ----------------------------------
    // Now that we KNOW there's nothing already there with that request number (for that
    // server ID and Nym ID), we go ahead and add the new message to the map. (And take ownership.)
    //
    m_mapMessages.insert(std::pair<int64_t, OTMessage *>(lRequestNum, &theMessage));
    // ----------------------------------
    //
    // Save it to local storage, in case we don't see the reply until the next run.
    //
    bool bAlreadyExists=false, bIsNewFolder=false;
    OTString strFolder, strFolder1, strFolder2;
    strFolder1.Format("%s%s%s",
                      OTFolders::Nym().Get(),               OTLog::PathSeparator(),
                      theMessage.m_strServerID.Get());
    strFolder2.Format("%s%s%s", strFolder1.Get(), OTLog::PathSeparator(),
                      "sent" /*todo hardcoding*/);
    // ----------------------------------
    strFolder.Format("%s%s%s", strFolder2.Get(), OTLog::PathSeparator(),
                     theMessage.m_strNymID.Get());
    // ----------------------------------

	OTString strFolderPath = "", strFolder1Path = "", strFolder2Path = "";

	OTPaths::AppendFolder(strFolderPath,  m_strDataFolder,strFolder );
	OTPaths::AppendFolder(strFolder1Path, m_strDataFolder,strFolder1);
	OTPaths::AppendFolder(strFolder2Path, m_strDataFolder,strFolder2);

	OTPaths::ConfirmCreateFolder(strFolderPath,bAlreadyExists,bIsNewFolder);
	OTPaths::ConfirmCreateFolder(strFolder1Path,bAlreadyExists,bIsNewFolder);
	OTPaths::ConfirmCreateFolder(strFolder2Path,bAlreadyExists,bIsNewFolder);
    
    OTString strFile;
    strFile.Format("%s.msg", theMessage.m_strRequestNum.Get());
    
    theMessage.SaveContract(strFolder.Get(), strFile.Get());
    // ----------------------------------
    // We also keep a list of the request numbers, so let's load it up, add the number
    // to that list, and then save it again.
    //
    OTNumList theNumList;
    std::string str_data_filename("sent.dat"); // todo hardcoding.
    if (OTDB::Exists(strFolder.Get(), str_data_filename))
    {
        OTString strNumList(OTDB::QueryPlainString(strFolder.Get(), str_data_filename));
        if (strNumList.Exists())
            theNumList.Add(strNumList);
        theNumList.Add(lRequestNum); // Add the new request number to it.
    }
    else // it doesn't exist on disk, so let's just create it from the list we have in RAM so we can store it to disk.
    {
        it = m_mapMessages.begin();
        while (it != m_mapMessages.end())
        {
            // -----------------------------
            const int64_t  & lTempReqNum   = it->first;
            // -----------------------
            OTMessage   * pMsg          = it->second;
            OT_ASSERT(NULL != pMsg);
            // -----------------------------
            //
            // If a server ID was passed in, but doesn't match the server ID on this message,
            // Then skip this one. (Same with the NymID.)
            //
            if (!theMessage.m_strServerID.Compare(pMsg->m_strServerID) ||
                !theMessage.m_strNymID.   Compare(pMsg->m_strNymID))
            {
                ++it;
                continue;
            }
            // --------
            else
            {
                theNumList.Add(lTempReqNum);
            }
            ++it;
        }
    }// else
    // ----------------------------------
    // By this point, theNumList has either been loaded from local storage and had the new number added,
    // or it wasn't in local storage and thus we created it and added all the numnbers to it (including new one.)
    // Therefore nothing left to do here, but save it back again!
    //
    OTString strOutput;
    theNumList.Output(strOutput);
    
    if (!OTDB::StorePlainString(strOutput.Get(), strFolder.Get(), str_data_filename)) // todo hardcoding.
    {
        OTLog::Error("OTMessageOutbuffer::AddSentMessage: Error: failed writing list of request numbers to storage.\n");
    }
}
/// True/False, based on whether ANY of the numbers in rhs are found in *this.
///
bool OTNumList::VerifyAny(const OTNumList & rhs) const
{
    return rhs.VerifyAny(m_setData);
}