/*
 * 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 <errno.h>

#include <bp/common.h>
#include <bp/io.h>

#include "merge-output.h"

int merge_output_flush(struct merge_output *output)
{
   int rc;
   output->blocks_written++;
   rc = fseeko(output->fobs.file, (off64_t)output->current_block *
      output->fobs.block_size, SEEK_SET);
   if (rc != 0)
      return rc;
   rc = file_obs_flush(&output->fobs);
   return rc;
}

static int merge_output_add_bit(void *o, int bit)
{
   int rc;
   struct merge_output *output = (struct merge_output *)o;
   file_obs_add_bit_noflush(&output->fobs, bit);
   if (output->fobs.bits_used == 8 * output->fobs.block_size) {
      rc = merge_output_flush(output);
      if (rc != 0)
         return rc;
      output->current_block = free_block_list_alloc(output->block_list);
   }
   return 0;
}

void merge_output_create(struct merge_output *output,
   struct occurrences *occurrences, struct free_block_list *block_list,
   FILE *offsets)
{
   file_obs_create(&output->fobs, occurrences->bs_backend.block_size, 
      occurrences->file);
   output->blocks_written = 0;
   output->block_list = block_list;
   output->current_block = free_block_list_alloc(output->block_list);
   output->frequencies = occurrences->frequencies;
   output->obs.data = output;
   output->obs.add_bit = merge_output_add_bit;
   output->current.term = -1;
   output->current.document = 0;
   output->num_documents = occurrences->num_documents;
   output->offsets = offsets;
}

void merge_output_destroy(struct merge_output *output)
{
   vector_destroy(output->frequencies);   
   free(output->frequencies);
   file_obs_destroy(&output->fobs);
}

void merge_output_occurrence(struct merge_output *output,
   struct occurrence *what)
{
   uint32_t num;
   if (output->current.term != what->term) {
      while (output->current.term + 1 < what->term) {
         /* Allow non-consecutive terms; value of offset doesn't matter. */
         uint32_t offset = 0;
         bp_write(output->offsets, &offset, sizeof offset, 1);
         output->current.term++;
      }
      uint32_t freq = vector_get(output->frequencies, what->term);
      
      if (output->blocks_written * output->fobs.block_size > (((uint32_t) -1) - output->fobs.bits_used) / 8) {
         /* Prevent from integer overflow: */
         errno = EFBIG;
         bp_system_error("index grew too large; please retry with higher granularity");
      }
      uint32_t offset = 8 * output->blocks_written * output->fobs.block_size +
         output->fobs.bits_used;
      output->b = get_golomb_parameter(output->num_documents, freq);
      encode_gamma(&output->obs, freq);
      num = what->document + 1;
      offset = en4(offset);
      bp_write(output->offsets, &offset, sizeof offset, 1);
   } else
      num = what->document - output->current.document;
   encode_golomb(&output->obs, num, output->b);
   output->current = *what;
}

off64_t merge_output_get_bytes_count(struct merge_output *output)
{
   return (off64_t)output->blocks_written * output->fobs.block_size +
      (output->fobs.bits_used + 7) / 8;
}

