/*
 * This file is part of the Poliqarp suite.
 * 
 * Copyright (C) 2004-2009 by Instytut Podstaw Informatyki Polskiej
 * Akademii Nauk (IPI PAN; Institute of Computer Science, Polish
 * Academy of Sciences; cf. www.ipipan.waw.pl).  All rights reserved.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file gpl.txt included in the packaging
 * of this file.  (See http://www.gnu.org/licenses/translations.html for
 * unofficial translations.)
 * 
 * A commercial license is available from IPI PAN (contact
 * Michal.Ciesiolka@ipipan.waw.pl or ipi@ipipan.waw.pl for more
 * information).  Licensees holding a valid commercial license from IPI
 * PAN may use this file in accordance with that license.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
 * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

#include <poliqarp-config.h>

#include <bp/common.h>
#include "runs.h"

struct run {
   struct runs *common;
   uint8_t *block;
   struct occurrence current;
   bool fresh;
   size_t remaining;
   size_t bit_number;
   off64_t block_offset;
   struct ibs ibs;
};

static void run_read_block(struct run *run)
{
   fseeko(run->common->file, run->block_offset, SEEK_SET);
   fread(run->block, run->common->block_size, 1, run->common->file);
   free_block_list_append(run->common->block_list, run->block_offset /
      run->common->block_size);
   run->block_offset += run->common->block_size;
   run->bit_number = 0;
}

static int run_next_bit(void *r)
{
   struct run *run = (struct run *)r;
   int res = run->block[run->bit_number / 8] & (1 << (run->bit_number % 8));
   if (++run->bit_number == run->common->block_size * 8)
      run_read_block(run);
   return res;
}

int runs_fetch_occurrence(struct runs *runs, size_t which)
{
   uint32_t diff_term, diff_document;
   struct run *run = runs->data + which;
   if (!run->remaining)
      return -1;
   run->remaining--;
   diff_term = decode_unary(&run->ibs) - 1;
   diff_document = decode_delta(&run->ibs);
   if (diff_term || run->fresh) {
      run->current.document = diff_document - 1;
      run->current.term += diff_term;
   } else
      run->current.document += diff_document;
   run->fresh = false;
   return 0;
}

static void run_create(struct runs *runs, size_t i, off64_t offset, size_t size)
{
   struct run *run = runs->data + i;
   run->common = runs;
   run->block = bp_malloc(runs->block_size);
   run->ibs.data = runs->data + i;
   run->ibs.next_bit = run_next_bit;
   run->current.term = run->current.document = 0;
   run->fresh = true;
   run->block_offset = offset;
   run->remaining = size;
   run_read_block(run);
   runs_fetch_occurrence(runs, i);
}

void runs_create(struct runs *runs, struct occurrences *occurrences,
   struct free_block_list *block_list)
{
   size_t i;

   runs->size = vector_length(&occurrences->offsets) - 1;
   runs->data = bp_malloc(runs->size * sizeof *runs->data);
   runs->file = occurrences->file;
   runs->block_size = occurrences->bs_backend.block_size;
   runs->block_list = block_list;
   for (i = 0; i < runs->size; i++) {
      run_create(runs, i, vector_get(&occurrences->offsets, i),
         (i == runs->size - 1) ? occurrences->remaining : occurrences->size);
   }
}

static inline void run_destroy(struct runs *runs, size_t i)
{
   free(runs->data[i].block);
}

void runs_destroy(struct runs *runs)
{
   size_t i;
   for (i = 0; i < runs->size; i++)
      run_destroy(runs, i);
   free(runs->data);
}

struct occurrence *runs_get_current(struct runs *runs, size_t which)
{
   return &runs->data[which].current;
}

