/* annotation-builder.c:
 *
 * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
 ****************************************************************
 * Copyright (C) 2005 Canonical Limited
 *        Authors: Robert Collins <robert.collins@canonical.com>
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/bugs/exception.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/errno.h"
#include "hackerlab/char/str.h"
#include "libarch/annotation-builder.h"

/* local static functions */
static int arch_annotation_builder_destructor (void * data);

static int
arch_annotation_builder_find_file (arch_annotation_builder_t *builder, t_uchar const *file_id)
{
    int position;
    for (position = 0; position < ar_size_arch_annotated_file (builder->files); ++position)
        if (!str_cmp (file_id, builder->files[position]->file_id))
            return position;
    return -1;
}

void
arch_annotation_builder_init (arch_annotation_builder_t *builder)
{
    builder->files = NULL;
    builder->current_file = NULL;
    builder->current_patch = NULL;
}

int
arch_annotation_builder_destructor (void * data)
{
    arch_annotation_builder_t * builder = talloc_get_type (data, arch_annotation_builder_t);
    invariant (!!builder);
    arch_annotation_builder_finalise (builder);
    return 0;
}

arch_annotation_builder_t *
arch_annotation_builder_new (void * context)
{
    arch_annotation_builder_t * builder = talloc (context, arch_annotation_builder_t);
    arch_annotation_builder_init (builder);
    talloc_set_destructor (builder, arch_annotation_builder_destructor);
    return builder;
}

void 
arch_annotation_builder_finalise (arch_annotation_builder_t *builder)
{
    arch_patch_delete (&builder->current_patch);
}

/**
 * \@brief note a file by its observed id, and the number of lines we need to annotate.
 */
void
arch_annotation_builder_add_file (arch_annotation_builder_t *builder, t_uchar const *file_id, int size_if_start)
{
    int position = arch_annotation_builder_find_file (builder, file_id);
    
    if (position != -1)
      {
        builder->current_file = builder->files[position];
        return;
      }
    invariant (size_if_start != -1);
    ar_push_arch_annotated_file (&builder->files, arch_annotated_file_new (talloc_context, file_id, size_if_start));
    builder->current_file = builder->files[ar_size_arch_annotated_file (builder->files) - 1];
    talloc_steal (ar_base (builder->files), builder->current_file);
    talloc_steal (builder, ar_base (builder->files));
}

int
arch_annotation_builder_has_file (arch_annotation_builder_t *builder, t_uchar const *file_id)
{
    return arch_annotation_builder_find_file(builder, file_id) != -1;
}

/**
 * \brief retrieve and remove the annotated file for file_id
 * \throw ENOENT if file_id is not in the annotated files list
 * \param builder the builder
 * \param context the talloc context for the returned file object
 * \param file_id the file to retrieve
 * \return an arch_annotated_file_t *
 */
arch_annotated_file_t *
arch_annotation_builder_get_file (arch_annotation_builder_t *builder, void * context, t_uchar const *file_id)
{
    arch_annotated_file_t *result;
    int position = arch_annotation_builder_find_file (builder, file_id);
    if (position == -1)
        Throw (exception (ENOENT, "arch_annotation_builder_get_file: No such file."));
    result = *ar_ref_arch_annotated_file (&builder->files, position);
    for (; position < ar_size_arch_annotated_file (builder->files) - 1; ++position )
      {
        *ar_ref_arch_annotated_file (&builder->files, position) = *ar_ref_arch_annotated_file (&builder->files, position + 1);
      }
    ar_pop_arch_annotated_file (&builder->files);
    builder->current_file = NULL;
    return talloc_steal (context, result);
}

/**
 * \@brief add a patch 
 */
void 
arch_annotation_builder_add_patch (arch_annotation_builder_t *builder, arch_patch_id *patch, t_uchar const * creator)
{
    arch_patch_delete (&builder->current_patch);
    builder->current_patch = arch_patch_new (patch->patch_id);
    arch_patch_creator_set (builder->current_patch, creator);
}

void
arch_annotation_builder_subtract_line (arch_annotation_builder_t *builder, int old_offset)
{
    invariant (builder->current_file != NULL);
    invariant (builder->current_patch != NULL);
    arch_annotated_file_note_subtract (builder->current_file, old_offset);
}

void
arch_annotation_builder_add_line (arch_annotation_builder_t *builder, int line)
{
    invariant (builder->current_file != NULL);
    invariant (builder->current_patch != NULL);
    arch_annotated_file_note_line (builder->current_file, line, builder->current_patch, 0);

}

void
arch_annotation_builder_process_changes(arch_annotation_builder_t *builder, patch_line_changes_list changes)
{
    int position;
    for (position = 0; position < ar_size_patch_line_change (changes); ++ position)
      {
        if (changes[position].operation == PATCH_ADD_LINE)
          {
            arch_annotation_builder_add_line(builder, changes[position].offset);
          }
        else
          {
            arch_annotation_builder_subtract_line(builder, changes[position].offset);
          }
      }
}


syntax highlighted by Code2HTML, v. 0.9.1