/*
 * Copyright (c) 2002-2004, Index Data.
 * See the file LICENSE for details.
 * 
 * $Id: yaz-z-cache.cpp,v 1.15 2006/03/29 13:14:18 adam Exp $
 */

#include <yaz/log.h>
#include <yaz/proto.h>
#include <yazpp/record-cache.h>

using namespace yazpp_1;

struct yazpp_1::RecordCache_Entry {
    int m_offset;
    Z_NamePlusRecord *m_record;
    Z_RecordComposition *m_comp;
    RecordCache_Entry *m_next;
};

RecordCache::RecordCache ()
{
    m_mem = nmem_create();
    m_entries = 0;
    m_presentRequest = 0;
    m_searchRequest = 0;
    m_max_size = 200000;
}

RecordCache::~RecordCache ()
{
    nmem_destroy(m_mem);
}

void RecordCache::set_max_size(int sz)
{
    m_max_size = sz;
}

void RecordCache::clear ()
{
    nmem_destroy(m_mem);
    m_mem = nmem_create();
    m_entries = 0;
    m_presentRequest = 0;
    m_searchRequest = 0;
}

void RecordCache::copy_searchRequest(Z_SearchRequest *sr)
{
    ODR encode = odr_createmem(ODR_ENCODE);
    ODR decode = odr_createmem(ODR_DECODE);

    m_searchRequest = 0;
    m_presentRequest = 0;
    int v = z_SearchRequest (encode, &sr, 1, 0);
    if (v)
    {
        int len;
        char *buf = odr_getbuf(encode, &len, 0);
        odr_setbuf(decode, buf, len, 0);
        z_SearchRequest(decode, &m_searchRequest, 1, 0);
        nmem_transfer(m_mem, decode->mem);
    }
    odr_destroy(encode);
    odr_destroy(decode);
}

void RecordCache::copy_presentRequest(Z_PresentRequest *pr)
{
    ODR encode = odr_createmem(ODR_ENCODE);
    ODR decode = odr_createmem(ODR_DECODE);
    
    m_searchRequest = 0;
    m_presentRequest = 0;
    int v = z_PresentRequest (encode, &pr, 1, 0);
    if (v)
    {
        int len;
        char *buf = odr_getbuf(encode, &len, 0);
        odr_setbuf(decode, buf, len, 0);
        z_PresentRequest(decode, &m_presentRequest, 1, 0);
        nmem_transfer(m_mem, decode->mem);
    }
    odr_destroy(encode);
    odr_destroy(decode);
}

void RecordCache::add (ODR o, Z_NamePlusRecordList *npr, int start,
                           int hits)
{
    if (nmem_total(m_mem) > m_max_size)
        return;
    // Build appropriate compspec for this response
    Z_RecordComposition *comp = 0;
    if (hits == -1 && m_presentRequest)
        comp = m_presentRequest->recordComposition;
    else if (hits > 0 && m_searchRequest)
    {
        Z_ElementSetNames *esn;

        if (hits <= *m_searchRequest->smallSetUpperBound)
            esn = m_searchRequest->smallSetElementSetNames;
        else
            esn = m_searchRequest->mediumSetElementSetNames;
        comp = (Z_RecordComposition *) nmem_malloc(m_mem, sizeof(*comp));
        comp->which = Z_RecordComp_simple;
        comp->u.simple = esn;
    }

    // Z_NamePlusRecordList *npr to be owned by m_mem..
    NMEM tmp_mem = odr_extract_mem(o);
    nmem_transfer(m_mem, tmp_mem);
    nmem_destroy(tmp_mem);
    
    // Insert individual records in cache
    int i;
    for (i = 0; i<npr->num_records; i++)
    {
        RecordCache_Entry *entry = (RecordCache_Entry *)
            nmem_malloc(m_mem, sizeof(*entry));
        entry->m_record = (Z_NamePlusRecord *)
            nmem_malloc(m_mem, sizeof(*entry->m_record));
        entry->m_record->databaseName = npr->records[i]->databaseName;
        entry->m_record->which = npr->records[i]->which;
        entry->m_record->u.databaseRecord  = npr->records[i]->u.databaseRecord;
        entry->m_comp = comp;
        entry->m_offset = i + start;
        entry->m_next = m_entries;
        m_entries = entry;
    }
}

int RecordCache::match (RecordCache_Entry *entry,
                            Odr_oid *syntax, int offset,
                            Z_RecordComposition *comp)
{
    // See if our compspec match...
    int match = 0;
    ODR o1 = odr_createmem(ODR_ENCODE);
    ODR o2 = odr_createmem(ODR_ENCODE);
    
    z_RecordComposition(o1, &comp, 1, 0);
    z_RecordComposition(o2, &entry->m_comp, 1, 0);

    int len1 = -1;
    char *buf1 = odr_getbuf(o1, &len1, 0);
    int len2 = -1;
    char *buf2 = odr_getbuf(o2, &len2, 0);
    
    if (buf1 && buf2 && len1 && len1 == len2 && !memcmp(buf1, buf2, len1))
        match = 1;
    else if (!buf1 && !buf2 && !len1 && !len2)
        match = 1;
    
    odr_destroy(o1);
    odr_destroy(o2);
    if (!match)
        return 0;
    if (!syntax)
        return 0;
    // See if offset, OID match..
    if (entry->m_offset == offset &&
        entry->m_record->which == Z_NamePlusRecord_databaseRecord &&
        !oid_oidcmp(entry->m_record->u.databaseRecord->direct_reference,
                    syntax))
        return 1;
#if 0
    char mstr1[100];
    oid_to_dotstring(entry->m_record->u.databaseRecord->direct_reference, mstr1);
    char mstr2[100];
    oid_to_dotstring(syntax, mstr2);
    yaz_log(YLOG_LOG, "match fail 3 d=%s s=%s", mstr1, mstr2);
#endif

    return 0;
}

int RecordCache::lookup (ODR o, Z_NamePlusRecordList **npr,
                             int start, int num,
                             Odr_oid *syntax,
                             Z_RecordComposition *comp)
{
    int i;
    yaz_log(YLOG_DEBUG, "cache lookup start=%d num=%d", start, num);

    for (i = 0; i<num; i++)
    {
        RecordCache_Entry *entry = m_entries;
        for(; entry; entry = entry->m_next)
            if (match(entry, syntax, start+i, comp))
                break;
        if (!entry)
            return 0;
    }
    *npr = (Z_NamePlusRecordList *) odr_malloc(o, sizeof(**npr));
    (*npr)->num_records = num;
    (*npr)->records = (Z_NamePlusRecord **)
        odr_malloc(o, num * sizeof(Z_NamePlusRecord *));
    for (i = 0; i<num; i++)
    {
        RecordCache_Entry *entry = m_entries;
        for(; entry; entry = entry->m_next)
            if (match(entry, syntax, start+i, comp))
                break;
        if (!entry)
            return 0;
        (*npr)->records[i] = (Z_NamePlusRecord *)
            odr_malloc(o, sizeof(Z_NamePlusRecord));
        (*npr)->records[i]->databaseName = entry->m_record->databaseName;
        (*npr)->records[i]->which = entry->m_record->which;
        (*npr)->records[i]->u.databaseRecord =
            entry->m_record->u.databaseRecord;
    }
    return 1;
}
/*
 * Local variables:
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * End:
 * vim: shiftwidth=4 tabstop=8 expandtab
 */



syntax highlighted by Code2HTML, v. 0.9.1