#include "KinoSearch/Util/ToolSet.h"

#define KINO_WANT_COMPOUNDFILEREADER_VTABLE
#include "KinoSearch/Index/CompoundFileReader.r"

#include "KinoSearch/InvIndex.r"
#include "KinoSearch/Index/IndexFileNames.h"
#include "KinoSearch/Index/SegInfo.r"
#include "KinoSearch/Store/InStream.r"

CompoundFileReader*
CFReader_new(InvIndex *invindex, SegInfo *seg_info)
{
    ByteBuf *filename = BB_CLONE(seg_info->seg_name);
    Hash *metadata 
        = (Hash*)SegInfo_Extract_Metadata(seg_info, "compound_file", 13);
    CREATE(self, CompoundFileReader, COMPOUNDFILEREADER);

    /* check format */
    if (Hash_Fetch_I64(metadata, "format", 6) > COMPOUND_FILE_FORMAT) {
        CONFESS("Unsupported compound file format: %ld", 
            (long)Hash_Fetch_I64(metadata, "format", 6));
    }

    /* assign */
    self->invindex  = REFCOUNT_INC(invindex);
    self->seg_info  = REFCOUNT_INC(seg_info);

    /* retrieve entries */
    self->entries = (Hash*)Hash_Fetch(metadata, "sub_files", 9);
    if (self->entries == NULL)
        CONFESS("Failed to extract 'sub_files' from compound_file metadata");
    (void)REFCOUNT_INC(self->entries);

    /* open an instream which we'll clone over and over */
    BB_Cat_Str(filename, ".cf", 3);
    self->instream = Folder_Open_InStream(invindex->folder, filename);
    REFCOUNT_DEC(filename);

    return self;
}

void
CFReader_destroy(CompoundFileReader *self)
{
    REFCOUNT_DEC(self->invindex);
    REFCOUNT_DEC(self->instream);
    REFCOUNT_DEC(self->entries);
    REFCOUNT_DEC(self->seg_info);
    free(self);
}

static void
retrieve_offset_and_len(CompoundFileReader *self, const ByteBuf *filename,
                        u64_t *offset, u64_t *len)
{
    Hash *entry = (Hash*)Hash_Fetch_BB(self->entries, filename);
    if (entry == NULL)
        CONFESS("Couldn't find entry for '%s'", filename->ptr);
    
    *offset = Hash_Fetch_I64(entry, "offset", 6);
    *len    = Hash_Fetch_I64(entry, "length", 6);
}

InStream*
CFReader_open_instream(CompoundFileReader *self, const ByteBuf *filename)
{
    u64_t len;
    u64_t offset;

    retrieve_offset_and_len(self, filename, &offset, &len);

    return InStream_Reopen(self->instream, filename, offset, len);
}

ByteBuf*
CFReader_slurp_file(CompoundFileReader *self, const ByteBuf *filename)
{
    u64_t len;
    u64_t offset;
    ByteBuf *retval;

    retrieve_offset_and_len(self, filename, &offset, &len);
    retval = BB_new(len);
    InStream_SSeek(self->instream, offset);
    InStream_Read_Bytes(self->instream, retval->ptr, len);
    retval->len = len;
    
    return retval;
}

bool_t
CFReader_file_exists(CompoundFileReader *self, const ByteBuf *filename)
{
    Hash *entry = (Hash*)Hash_Fetch_BB(self->entries, filename);
    if (entry == NULL)
        return false;
    else 
        return true;
}

VArray*
CFReader_list(CompoundFileReader *self) {
    return Hash_keys(self->entries);
}

void
CFReader_close_f(CompoundFileReader *self)
{
    InStream_SClose(self->instream);
}

/* Copyright 2006-2007 Marvin Humphrey
 *
 * This program is free software; you can redistribute it and/or modify
 * under the same terms as Perl itself.
 */