/*
 * Returns true iff 
 */
bool 
CacheStrategyUtility::isResponsibleForDataObject(
    DataObjectRef &dObj)
{
    string id = string(dObj->getIdStr());
/*
    // THIS IS NOT THREAD SAFE!! added getOrigSize
    // to grab unaltered file size.
    if (utilMetadata.find(id) != utilMetadata.end()) {
        return true;
    }
*/

    if (dObj->isDuplicate()) {
        return false;
    }

    // SW: TODO: NOTE: this might not be the best way to check if it's from
    // a local application, but it works for now...
    bool isLocal = dObj->getRemoteInterface() && dObj->getRemoteInterface()->isApplication();

    bool notResponsible = dObj->isControlMessage() || dObj->isNodeDescription();
    bool isResponsible = !notResponsible;

    if (!handle_zero_size) {
        isResponsible = isResponsible && (dObj->getOrigDataLen() > 0);
    }

    if (!manage_locally_sent_files) {
        isResponsible = isResponsible && dObj->isPersistent();
    }

    if (stats_replacement_strat && stats_replacement_strat->isResponsibleForDataObject(dObj)) {
        isResponsible = true;
    } else if (manage_only_remote_files) {
        isResponsible = isResponsible && !isLocal;
    }
    return isResponsible;
}
/*
 * Callback when a new data object is inserted. Updates the utility strategy
 * state and notifies the utility functions.
 */
void
CacheStrategyUtility::_handleNewDataObject(
    DataObjectRef &dObj)
{
    if (!isResponsibleForDataObject(dObj)) {
        //HAGGLE_DBG("Ignoring data object, in-eligible for caching\n");
        return;
    }

    if (stats_replacement_strat && stats_replacement_strat->isResponsibleForDataObject(dObj)) {
        stats_replacement_strat->handleNewDataObject(dObj);
        return;
    }

    string id = string(dObj->getIdStr());

    if (utilMetadata.find(id) != utilMetadata.end()) {
        // the DO is already in the cache!
        HAGGLE_DBG("Received data object already in the cache: %s\n", id.c_str());
        current_dupe_do_recv++;
        return;
    }

    getUtilityFunction()->notifyInsertion(dObj);

    int cost = dObj->getOrigDataLen();
    // NOTE: we handle zero sized data objects by "faking"
    // a size of 1.
    if (cost == 0) {
        cost = 1; 
    }

    string strResults="";
    if (Trace::trace.getTraceType() == TRACE_TYPE_DEBUG2) {
        strResults.append(id);
        strResults.append("[I]=");
    }
    double utiltiy = getUtilityFunction()->compute(id, strResults);
    HAGGLE_DBG2("%s --> %f\n", strResults.c_str(), utiltiy);
    Timeval now = Timeval::now();

    bool purgeNow = false;
    if (utiltiy < getGlobalOptimizer()->getMinimumThreshold()) {
        HAGGLE_DBG("Minimum threshold for incoming data object %s is insufficient: %f < %f\n", id.c_str(), utiltiy, getGlobalOptimizer()->getMinimumThreshold());
        current_drop_on_insert++;
        purgeNow = true;
    }

    if (!purgeOnInsert && ((current_size + cost) > max_capacity_kb*1024))  {
        HAGGLE_DBG("Cache is full and purge on insert is disabled, evicting new data object!\n");
        purgeNow = true;
        total_do_hard_evicted++;
        total_do_hard_evicted_bytes += cost;
    }

    if (purgeNow) {
        // we need to properly remove from bloomfilter even when capacity is violated
        getUtilityFunction()->notifyDelete(dObj);

        // CBMEN, HL - Begin
        // Remove any pending send events for this data object
        HAGGLE_STAT("purging send events for dObj %s\n", dObj->getIdStr());
        getManager()->getKernel()->cancelEvents(EVENT_TYPE_DATAOBJECT_SEND, dObj);
        // CBMEN, HL, End

        // delayed delete (although never inserted)
        if (!keep_in_bloomfilter) {
            int delay = (bloomfilter_remove_delay_ms <= 0) ? 1000 : bloomfilter_remove_delay_ms;
            DataObjectId_t *heapId = (DataObjectId_t *)malloc(sizeof(DataObjectId_t));
            DataObject::idStrToId(id, *heapId);
            getManager()->getKernel()->addEvent(new Event(bloomfilterRemoveDelayEventType, heapId, delay/(double)1000));
        }

        return;
    }

    DataObjectUtilityMetadata *do_metadata = new DataObjectUtilityMetadata(
        dObj,
        id,
        cost,
        utiltiy,
        now,
        dObj->getCreateTime());

    if (purgeOnInsert) {
        do_metadata->setEnableDeletion(false);
    }

    if (false == utilMetadata.insert(make_pair(id, do_metadata)).second) {
        HAGGLE_ERR("Somehow data object already in cache\n"); 
        delete do_metadata;
        return;
    }

    current_size += cost;
    current_num_do++;
    total_do_inserted++;
    total_do_inserted_bytes += cost;

    if (!purgeOnInsert) {
        getManager()->insertDataObjectIntoDataStore(dObj);
        return;
    }

    bool was_deleted = false;
    _purgeCache(id, &was_deleted);

    if (was_deleted) {
        HAGGLE_DBG("Purged incoming data object %s on insert.\n", id.c_str());
        current_drop_on_insert++;
        return;
    }

    // DO still in cache, mark to allow deletion in future
    do_metadata->setEnableDeletion(true);
    getManager()->insertDataObjectIntoDataStore(dObj);
}