
#ifndef history_c_INCLUDED
#define history_c_INCLUDED

#include <string.h>
#include <stdlib.h>

#include "../c/screen_interface.h"
#include "history.h"
#include "tracelog.h"
#include "config.h"
#include "i18n.h"
#include "z_ucs.h"
#include "types.h"
#include "fizmo.h"
#include "wordwrap.h"
#include "splint.h"


outputhistory_ptr outputhistory[]
= { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };

static WORDWRAP *scrollback_count_wordwrapper = NULL;

static void set_text_style_count(z_style text_style);
static void set_colour_count(z_colour foreground, z_colour background,
    int16_t window);
static void set_font_count(z_font font_type);
static void z_ucs_output_count(z_ucs *z_ucs_output);
static void repeat_starts_from_buffer_begin();
static int scrollback_counter_lines;
static int scrollback_counter_chars;
static int scrollback_width;


static struct history_repetition_target repetition_counting =
{
  &set_text_style_count,
  &set_colour_count,
  &set_font_count,
  &z_ucs_output_count,
  &repeat_starts_from_buffer_begin
};


static void set_text_style_count(z_style UNUSED(text_style))
{
}


static void set_colour_count(z_colour UNUSED(foreground),
    z_colour UNUSED(background), int16_t UNUSED(window))
{
}


static void set_font_count(z_font UNUSED(font_type))
{
}


static void z_ucs_output_count(z_ucs *z_ucs_output)
{
    wordwrap_wrap_z_ucs(scrollback_count_wordwrapper, z_ucs_output);
}


static void repeat_starts_from_buffer_begin()
{
}


static void z_ucs_output_count_destination(z_ucs *z_ucs_output,
    void *UNUSED(dummyparameter))
{
  z_ucs *newline_index;
  long len;

  TRACE_LOG("Counter received output: \"");
  TRACE_LOG_Z_UCS(z_ucs_output);
  TRACE_LOG("\".\n");

  TRACE_LOG("Current counter-lines: %d.\n", scrollback_counter_lines);

  // Calculate number of lines and chars in output.
  while (*z_ucs_output != 0)
  {
    newline_index = z_ucs_chr(z_ucs_output, Z_UCS_NEWLINE);

    if (newline_index != NULL)
    {
      len = newline_index - z_ucs_output + 1;
      scrollback_counter_lines++;
    }
    else
    {
      len = z_ucs_len(z_ucs_output);
      if (len >=
          scrollback_width + scrollback_count_wordwrapper->left_side_padding)
      {
        len
          = scrollback_width + scrollback_count_wordwrapper->left_side_padding;
        scrollback_counter_lines++;
      }
    }

    TRACE_LOG("length:%ld.\n", len);
    scrollback_counter_chars += len;
    z_ucs_output += len;
  }
}


static void history_do_nothing(void *UNUSED(destination_parameter))
{
}


static struct wordwrap_target history_wrapper_output_target =
{
  &z_ucs_output_count_destination,
  &history_do_nothing,
  &history_do_nothing
};


OUTPUTHISTORY *create_outputhistory(int window_number,
    size_t maximum_buffer_size, size_t buffer_increment_size,
    size_t metadata_increment_size, z_colour foreground_colour,
    z_colour background_colour, z_font font, z_style style)
{
  OUTPUTHISTORY *result;

  result = (OUTPUTHISTORY*)fizmo_malloc(sizeof(OUTPUTHISTORY));

  result->window_number = window_number;
  result->z_history_maximum_buffer_size = maximum_buffer_size;
  result->z_history_buffer_increment_size = buffer_increment_size;
  result->z_history_metadata_increment_size = metadata_increment_size;

  // z_history_buffer is a circular array. empty in case front=NULL,
  // full in case front+1==back.
  result->z_history_buffer = NULL;
  result->z_history_buffer_last_element = NULL;

  // We keep two sizes for the history buffer: The raw size, and the "real"
  // size, which is equal to the raw size minus a string-terminating zero.
  result->z_history_buffer_size = 0;
  result->z_history_buffer_raw_size = 0;

  // (F)ront-index is where the next (c)har will be stored.
  result->z_history_buffer_front_index = NULL;

  // (B)ack-index is where the last (c)har was stored.
  result->z_history_buffer_back_index = NULL;

  // non-full-buffer: [BccF........] 
  // full buffer:     [cccccFBccccc]

  result->history_metadata_buffer = NULL;
  result->history_buffer_metadata_size = 0;
  result->history_buffer_metadata_index = 0;

  // These four values keep the current state of the back-end index of the
  // history queue. This is necessary, since for example the command to set
  // colour to a given value has already been overwritten in the history,
  // but the text using the colour is still in the buffer.
  result->history_buffer_back_index_font = font;
  result->history_buffer_back_index_style = style;
  result->history_buffer_back_index_foreground = foreground_colour;
  result->history_buffer_back_index_background = background_colour;

  result->paragraph_pos_cache = NULL;
  result->pos_cache_size = 0;
  result->pos_cache_max_paragraph = 0;

  return result;
}

void destroy_outputhistory(/*@only@*/ OUTPUTHISTORY *history)
{
  /*@-compdestroy@*/ free(history->history_metadata_buffer) /*@+compdestroy@*/;
  free(history->z_history_buffer);
  free(history);
}


static size_t get_space_in_history_buffer(OUTPUTHISTORY *history)
{
  if (history->z_history_buffer_front_index == NULL)
  {
    return history->z_history_buffer_size;
  }
  else
  {
    if (history->z_history_buffer_front_index
        >= history->z_history_buffer_back_index)
      return
        history->z_history_buffer_size
        - (history->z_history_buffer_front_index
            - history->z_history_buffer_back_index + 1);
    return
      (size_t)(history->z_history_buffer_back_index
          - history->z_history_buffer_front_index - 1);
  }
}


// Inclusive from-index and to-index.
static void adjust_history_metadata(OUTPUTHISTORY *history, z_ucs *from_index,
    z_ucs *to_index)
{
  int index;

  if (history->history_metadata_buffer == NULL)
    return;

  TRACE_LOG("Adjusting history-metadata from %p to %p.\n",
      from_index, to_index);

  // Check if we had some metadata saved for the now lost part of the
  // history. If so, adjust the back-index-values accordingly.
  for (index=0; index<history->history_buffer_metadata_index; index++)
  {
    TRACE_LOG("Verifying metadata-entry %d.\n", index);

    if (
        (history->history_metadata_buffer[index].history_index == NULL)
        ||
        (
         (history->history_metadata_buffer[index].history_index >= from_index)
         &&
         (history->history_metadata_buffer[index].history_index <= to_index)
        )
       )
    {
      TRACE_LOG("Processing metadata-entry %d of type %d to rear-data.\n",
          index, history->history_metadata_buffer[index].metadata_type);

      if (history->history_metadata_buffer[index].metadata_type
          == HISTORY_METADATA_TYPE_FONT)
      {
        TRACE_LOG("New back-index-font: %d.\n",
            history->history_metadata_buffer[index].parameter1);
        history->history_buffer_back_index_font
          = history->history_metadata_buffer[index].parameter1;
      }
      else if (history->history_metadata_buffer[index].metadata_type
          == HISTORY_METADATA_TYPE_STYLE)
      {
        TRACE_LOG("New back-index-style: %d.\n",
            history->history_metadata_buffer[index].parameter1);
        history->history_buffer_back_index_style
          = history->history_metadata_buffer[index].parameter1;
      }
      else if (history->history_metadata_buffer[index].metadata_type
          == HISTORY_METADATA_TYPE_COLOUR)
      {
        TRACE_LOG("New back-index-colour: %d, %d.\n",
            history->history_metadata_buffer[index].parameter1,
            history->history_metadata_buffer[index].parameter2);
        history->history_buffer_back_index_foreground
          = history->history_metadata_buffer[index].parameter1;
        history->history_buffer_back_index_background
          = history->history_metadata_buffer[index].parameter2;
      }
    }
    else
    {
      TRACE_LOG("Entry %d is not behind current end-index.\n", index);

      // Since the entries are ordered by index in the queue, once
      // a single entry is behind the new index we know all others
      // will be behind as well. Thus, we can end the verification
      // here.

      break;
    }
  }

  if (index > 0)
  {
    if (history->history_buffer_metadata_index - index > 0)
    {
      TRACE_LOG("Moving %d metadata-entries to front of buffer.\n",
          history->history_buffer_metadata_index - index);

      TRACE_LOG("dest: %p.\n",
          history->history_metadata_buffer);

      TRACE_LOG("src: %p.\n",
          history->history_metadata_buffer + 
          history->history_buffer_metadata_index - index);

      TRACE_LOG("size: %zu.\n",
          (history->history_buffer_metadata_index - index)
          * sizeof(struct history_metadata));

      // In case we've processed at least one entry, we have to adjust
      // buffer contents accordingly.
      /*@-aliasunique@*/
      memmove(
          history->history_metadata_buffer,
          history->history_metadata_buffer + index,
          (history->history_buffer_metadata_index - index)
          * sizeof(struct history_metadata));
      /*@+aliasunique@*/

      history->history_buffer_metadata_index -= index;
    }
    else
      history->history_buffer_metadata_index = 0;
  }
}


static void adjust_z_history_back_index(OUTPUTHISTORY *history,
    z_ucs *new_index)
{
  z_ucs *ptr;
  z_ucs buf;

  TRACE_LOG("Adjusting history-back-index from %p to %p.\n",
      history->z_history_buffer_back_index, new_index);

  // To achieve a cleaner scrollback, we'll try to advance the end
  // of the buffer to the next newline character.
  if (history->z_history_buffer_front_index
      < history->z_history_buffer_back_index)
  {
    // wrap-around situation
    history->z_history_buffer_last_element[1] = 0;
    if ((ptr = z_ucs_chr(new_index, Z_UCS_NEWLINE)) != NULL)
      new_index = ptr;
    else
    {
      buf = *history->z_history_buffer_front_index;
      *history->z_history_buffer_front_index = 0;
      if ((ptr = z_ucs_chr(history->z_history_buffer, Z_UCS_NEWLINE)) != NULL)
      {
        new_index = ptr;
      }
      *history->z_history_buffer_front_index = buf;
    }
  }
  else
  {
    history->z_history_buffer_front_index[1] = 0;
    if ((ptr = z_ucs_chr(new_index, Z_UCS_NEWLINE)) != NULL)
      new_index = ptr;
  }

  if (
      (history->z_history_buffer == NULL)
      ||
      (history->z_history_buffer_back_index == NULL)
      ||
      (history->z_history_buffer_last_element == NULL)
     )
    return;

  if (new_index > history->z_history_buffer_back_index)
  {
    // In this case we're working linear on all entries from the back
    // end to the new backend-index.
    adjust_history_metadata(
        history,
        history->z_history_buffer_back_index,
        new_index - 1);
  }
  else
  {
    // In this case we have a wrap-around and process in two stages:

    // From the current index to the end of the buffer ...
    adjust_history_metadata(
        history,
        history->z_history_buffer_back_index, 
        history->z_history_buffer_last_element);

    // ... and from the start of the buffer to the new end-index.
    adjust_history_metadata(
        history,
        history->z_history_buffer,
        new_index - 1);
  }

  /*@-temptrans@*/ history->z_history_buffer_back_index /*@+temptrans@*/
    = new_index;
}


void store_z_ucs_output_in_history(OUTPUTHISTORY *history,
    z_ucs *z_ucs_output)
{
  size_t space_in_buffer;
  size_t new_buffer_size;
  z_ucs *current_history_buffer;
  size_t input_size;
  size_t chars_to_copy;
  z_ucs *target_index;
  int i;

  if (z_ucs_output == NULL)
    return;

  TRACE_LOG("Historizing '");
  TRACE_LOG_Z_UCS(z_ucs_output);
  TRACE_LOG("' for window %d.\n", history->window_number);

  TRACE_LOG("Front: %p, Back %p.\n", history->z_history_buffer_front_index,
      history->z_history_buffer_back_index);

  input_size = z_ucs_len(z_ucs_output);

  if (input_size == 0)
    return;

  history->pos_cache_max_paragraph = 0;

  space_in_buffer = get_space_in_history_buffer(history);

  TRACE_LOG("Space in buffer: %ld, input size: %zu.\n",
      space_in_buffer, input_size);

  // Check if we have to enlarge the history buffer. If so, try to allocate
  // more space.
  while (input_size > space_in_buffer)
  {
    TRACE_LOG("input_size %zu, space_in_buffer:%ld, raw: %ld, MAX: %d.\n",
        input_size,
        space_in_buffer,
        history->z_history_buffer_raw_size,
        Z_HISTORY_V5_WIN0_MAXIMUM_SIZE);

    if (history->z_history_buffer_raw_size < Z_HISTORY_V5_WIN0_MAXIMUM_SIZE)
    {
      new_buffer_size = history->z_history_buffer_raw_size
        + Z_HISTORY_V5_WIN0_INCREMENT_SIZE;

      if (new_buffer_size > Z_HISTORY_V5_WIN0_MAXIMUM_SIZE)
        new_buffer_size = Z_HISTORY_V5_WIN0_MAXIMUM_SIZE;

      TRACE_LOG("Allocating %ld bytes for history.\n", new_buffer_size);

      current_history_buffer = history->z_history_buffer;

      history->z_history_buffer = (z_ucs*)realloc(history->z_history_buffer,
          new_buffer_size * sizeof(z_ucs));

      if (history->z_history_buffer == NULL)
      {
        TRACE_LOG("Realloc failed.\n");

        // Not enough memory. Every stays the way it is, since normal operation
        // is still possible without enlarging the history, we'll just
        // continue wrapping around the end of the buffer as if the maximum
        // buffer size would have been reached.
        // The only critical point is the case in which we cannot allocate
        // any memory at all.

        if (/*@-usereleased@*/ current_history_buffer /*@+usereleased@*/
            == NULL)
          i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_REALLOC_P0D_RETURNED_NULL_PROBABLY_OUT_OF_MEMORY,
            -0x010e,
            (long int)new_buffer_size);
        else
          // Restore current buffer.
          history->z_history_buffer = current_history_buffer;
      }
      else
      {
        if (history->z_history_buffer_front_index != NULL)
        {
          history->z_history_buffer_front_index
            = history->z_history_buffer
            + (history->z_history_buffer_front_index
                - /*@-usereleased@*/ current_history_buffer /*@+usereleased@*/);

          history->z_history_buffer_back_index
            = history->z_history_buffer
            + (history->z_history_buffer_back_index - current_history_buffer);

          // If history_buffer_metadata_index is > 0, "history_metadata_buffer"
          // is not null, so it's okay to encapsulate all references to
          // "history_metadata_buffer" in "-nullderef".
          for (i=0; i<history->history_buffer_metadata_index; i++)
          {
            if (
                /*@-nullderef@*/
                history->history_metadata_buffer[i].history_index != NULL
                /*@+nullderef@*/
               )
            {
              // In case we have a regular entry, subtract start pointer
              // of old buffer from index to get pure relative index in
              // buffer, then add the returncode -- the start index of
              // the realloced buffer -- to calculate new absolute buffer
              // position.
              TRACE_LOG("Adjusting metadata-entry %p buffer position to %p.\n",
                  /*@-nullderef@*/
                  history->history_metadata_buffer[i].history_index
                  /*@+nullderef@*/ ,
                  history->z_history_buffer
                  + (
                    /*@-nullderef@*/history->history_metadata_buffer[i]
                    /*@+nullderef@*/.history_index
                    - history->z_history_buffer));

              /*@-nullderef@*/history->history_metadata_buffer[i]
                /*@+nullderef@*/.history_index
                = history->z_history_buffer
                + (
                    /*@-nullderef@*/history->history_metadata_buffer[i]
                    /*@+nullderef@*/.history_index
                    - current_history_buffer);
            }
            else
            {
              // In case the history index was NULL, meaning "the next valid"
              // position in the history buffer, we can now fix this
              // position.
              TRACE_LOG("Setting metadata-entry %p buffer position to %p.\n",
                  /*@-nullderef@*/history->history_metadata_buffer[i]
                  /*@+nullderef@*/.history_index,
                  history->z_history_buffer_front_index + 1);

              /*@-nullderef@*/history->history_metadata_buffer[i]
                /*@+nullderef@*/.history_index
                = history->z_history_buffer_front_index + 1;
            }
          }
        }

        // We must not use the last byte, it may be required to act as a
        // string-null-terminator in case we send output from the buffer.
        history->z_history_buffer_last_element
          = history->z_history_buffer + new_buffer_size - 2;

        history->z_history_buffer_raw_size = new_buffer_size;

        history->z_history_buffer_size = new_buffer_size - 1;

        space_in_buffer = get_space_in_history_buffer(history);

        TRACE_LOG("Pointers after realloc:\n");

        TRACE_LOG("z_history_buffer %p\n",
            history->z_history_buffer);
        TRACE_LOG("z_history_buffer_front_index %p\n",
            history->z_history_buffer_front_index);
        TRACE_LOG("z_history_buffer_last_element %p\n",
            history->z_history_buffer_last_element);
        TRACE_LOG("z_history_buffer_size %ld\n",
            history->z_history_buffer_size);
      }
    }
    else
    {
      break;
    }
  }

  if (input_size >= /*@-nullpass@*/ history->z_history_buffer_size)
  {
    TRACE_LOG("Storing over-sized input '");
    TRACE_LOG_Z_UCS(z_ucs_output);
    TRACE_LOG("'.\n");

    // In case the input is so gigantic -- or the buffer so tiny -- that
    // it consumes more space than the whole buffer has, we'll only save
    // as much as possible.
    z_ucs_output += input_size - space_in_buffer;
    TRACE_LOG("Cropped input to '");
    TRACE_LOG_Z_UCS(z_ucs_output);
    TRACE_LOG("'.\n");

    TRACE_LOG("Writing %ld chars to %p.\n", space_in_buffer,
        history->z_history_buffer);

    memcpy(
        z_ucs_output,
        history->z_history_buffer,
        space_in_buffer * sizeof(z_ucs));

    // Work-up the current history buffer completely -- first from the
    // current end the end to the real end of buffer space ...
    adjust_history_metadata(
        history,
        history->z_history_buffer_back_index,
        history->z_history_buffer_last_element);

    // ... next, in case we have a wrap around-situation, we also process
    // from the real front of the buffer to the current front index.
    if (history->z_history_buffer_front_index
        < history->z_history_buffer_back_index)
      adjust_history_metadata(
          history,
          history->z_history_buffer,
          history->z_history_buffer_front_index);

    history->z_history_buffer_front_index
      = history->z_history_buffer_last_element;
    history->z_history_buffer_back_index = 0;

    return;
  }

  // If we're arriving at this point, we know that the input size is less
  // than the history-buffer-size, since the section above stores such
  // over-sized input and returns after that.

  // We can now currectly calculate all metadata-indexes which are
  // currently set to NULL (meaning the next valid buffer position).
  // We'll start from the end of the buffer and work to the front,
  // since new entries are appended to the end.
  // First, we'll calculate the destination index. It's simply the position
  // after the current front index, or the start of the buffer in case
  // we're at the buffer end.
  if (history->z_history_buffer_front_index == NULL)
    target_index = history->z_history_buffer;
  else if (history->z_history_buffer_front_index
      == history->z_history_buffer_last_element)
    target_index = history->z_history_buffer;
  else
    target_index = history->z_history_buffer_front_index + 1;

  // If history_buffer_metadata_index is > 0, "history_metadata_buffer"
  // is not null, so it's okay to encapsulate all references to
  // "history_metadata_buffer" in "-nullderef".
  for (i=history->history_buffer_metadata_index-1; i>=0; i--)
  {
      TRACE_LOG("Metadata-entry %d points to %p.\n",
          i,
          /*@-nullderef@*/ history->history_metadata_buffer[i]
          /*@+nullderef@*/.history_index);
    
      if (/*@-nullderef@*/history->history_metadata_buffer[i]
          /*@+nullderef@*/.history_index == NULL)
    {
      // Here we've found an entry we now have to process.
      TRACE_LOG("Adjusting metadata %d buffer to %p after ucs-store.\n",
          i, target_index);
      /*@-onlytrans@*/
      /*@-nullderef@*/history->history_metadata_buffer[i]
        /*@+nullderef@*/.history_index = target_index;
      /*@+onlytrans@*/
    }
    else
    {
      break;
    }
  }

  // Check if we have to wrap around.
  if (history->z_history_buffer_front_index + input_size
      > history->z_history_buffer_last_element)
  {
    TRACE_LOG("Wrapping input '");
    TRACE_LOG_Z_UCS(z_ucs_output);
    TRACE_LOG("'.\n");
    // In this case we have a wrap-around situation. Thus, we'll first fill
    // up the buffer as much as possible.

    // At this point we know that the front-index cannot be NULL, since if
    // the buffer would have never been written to, we would not have a
    // wrap-around situation in case input-size < buffer-size.

    chars_to_copy
      = (size_t)(history->z_history_buffer_last_element
          - history->z_history_buffer_front_index);

    TRACE_LOG("Writing %ld chars to %p.\n",
        chars_to_copy, history->z_history_buffer_front_index + 1);

    /*@-mayaliasunique@*/ memcpy(
        history->z_history_buffer_front_index + 1,
        z_ucs_output,
        chars_to_copy * sizeof(z_ucs));

    z_ucs_output += chars_to_copy;
    input_size -= chars_to_copy;

    TRACE_LOG("Writing %zu chars to %p.\n",
        input_size, history->z_history_buffer);

    memcpy(
        history->z_history_buffer,
        z_ucs_output,
        input_size * sizeof(z_ucs));

    history->z_history_buffer_front_index
      = history->z_history_buffer + input_size - 1;

    adjust_z_history_back_index(history,
        history->z_history_buffer_front_index + 1);
  }
  else
  {
    // Here we can just write the input into the buffer and adjust
    // pointers accordingly.
    if (history->z_history_buffer_front_index == NULL)
    {
      // In this case we're writing the first input ever into a newly
      // allocated buffer.
      target_index = history->z_history_buffer;
      history->z_history_buffer_front_index
        = history->z_history_buffer + input_size - 1;

      /*@-onlytrans@*/
      history->z_history_buffer_back_index = history->z_history_buffer;
      /*@+onlytrans@*/
    }
    else
    {
      // Target-index can't ever be null.
      /*@-nullstate@*/ target_index = history->z_history_buffer_front_index + 1;

      if (history->z_history_buffer_back_index
          > history->z_history_buffer_front_index)

        adjust_z_history_back_index(
            history,
            history->z_history_buffer_back_index + input_size);

      history->z_history_buffer_front_index += input_size;
    }

    TRACE_LOG("Writing %d bytes to %p.\n", (int)input_size, target_index);

    memcpy(
        target_index,
        z_ucs_output,
        input_size * sizeof(z_ucs));
  }
}


void store_metadata_in_history(OUTPUTHISTORY *history, int metadata_type,
    int16_t parameter1, int16_t parameter2)
{
  size_t new_buffer_size;
  z_ucs *history_buffer_next_index;

  history->pos_cache_max_paragraph = 0;

  if ((size_t)history->history_buffer_metadata_index
      == history->history_buffer_metadata_size)
  {
    new_buffer_size
      = (history->history_buffer_metadata_size
          + Z_HISTORY_V5_WIN0_METADATA_INCREMENT_SIZE)
      * sizeof(struct history_metadata);

    TRACE_LOG("Allocating %zu bytes for history-metadata-buffer.\n",
        new_buffer_size);

    history->history_metadata_buffer
      = realloc(history->history_metadata_buffer, new_buffer_size);

    history->history_buffer_metadata_size
      = history->history_buffer_metadata_size
      + Z_HISTORY_V5_WIN0_METADATA_INCREMENT_SIZE;

    TRACE_LOG("Buffer starts at %p.\n", history->history_metadata_buffer);
  }

  TRACE_LOG("Writing new metadata-entry %d of type %d data to %p.\n",
      history->history_buffer_metadata_index,
      metadata_type,
      &history->history_metadata_buffer
      [history->history_buffer_metadata_index].history_index);

  // Since the metadata is valid for the next index in the history buffer,
  // we advance out target pointer to the next valid index position. This
  // works as long as we have still space for one more element in the
  // buffer.
  if (get_space_in_history_buffer(history) >= 1)
  {
    history_buffer_next_index = history->z_history_buffer_front_index + 1;
  }
  else
  {
    // In case we don't have enough space, we currently don't know if
    // the buffer will be enlarged or whether the buffer has been
    // allocated at all. In these cases we cannot yet determine the
    // next index position and only store a NULL pointer.
    history_buffer_next_index = NULL;
  }

  history->history_metadata_buffer
    [history->history_buffer_metadata_index].history_index
    = history_buffer_next_index;
  history->history_metadata_buffer
    [history->history_buffer_metadata_index].metadata_type
    = metadata_type;
  history->history_metadata_buffer
    [history->history_buffer_metadata_index].parameter1
    = parameter1;
  history->history_metadata_buffer[
    history->history_buffer_metadata_index].parameter2
    = parameter2;

  TRACE_LOG("New entry's history buffer index: %p\n",
      history->history_metadata_buffer
      [history->history_buffer_metadata_index].history_index);

  history->history_buffer_metadata_index++;
}


void store_paragraph_pos_in_cache(OUTPUTHISTORY *history, int paragraph_number,
    z_ucs *index)
{
  int new_size;
#ifdef ENABLE_TRACING
  int i;
#endif // ENABLE_TRACING

  TRACE_LOG("Storing paragraph position %d (size:%d).\n",
      paragraph_number, history->pos_cache_size);

  while (history->pos_cache_size <= paragraph_number)
  {
    new_size = history->pos_cache_size + 256;
    history->paragraph_pos_cache = (z_ucs**)fizmo_realloc(
        history->paragraph_pos_cache, sizeof(z_ucs*) * new_size);
    history->pos_cache_size = new_size;
    TRACE_LOG("paragraph_pos_cache resized to %d.\n", new_size);
  }

  history->paragraph_pos_cache[paragraph_number - 1] = index;

  if (paragraph_number > history->pos_cache_max_paragraph)
    history->pos_cache_max_paragraph = paragraph_number;

#ifdef ENABLE_TRACING
  for (i=0; i<history->pos_cache_max_paragraph; i++)
  {
    TRACE_LOG("cache contents(%d):%p\n",
        i+1, history->paragraph_pos_cache[i]);
  }
#endif // ENABLE_TRACING
}


/*@null@*/ /*@dependent@*/ static z_ucs *find_paragraphs_from_history(
    /*@-compdef@*/ OUTPUTHISTORY *history, int paragraph_to_find)
{
  z_ucs *z_history_buffer_index;
  z_ucs *back_index;
  z_ucs *z_history_output_start;
  bool final_pass_active;
  int paragraph_number = paragraph_to_find;

  if (paragraph_number < 0)
    return history->z_history_buffer_back_index;

  if (paragraph_number == 0)
    return NULL;

  TRACE_LOG("Cache (%d) at %p.\n", history->pos_cache_max_paragraph,
      history->paragraph_pos_cache);

  if (paragraph_number <= history->pos_cache_max_paragraph)
    return history->paragraph_pos_cache[paragraph_number - 1];

  if (history->z_history_buffer_front_index == NULL)
    return NULL;

  TRACE_LOG("Trying to find paragraph %d in history.\n",
      paragraph_number);

  TRACE_LOG("%p / %p / %p / %p\n",
      history->z_history_buffer,
      history->z_history_buffer_last_element,
      history->z_history_buffer_front_index,
      history->z_history_buffer_back_index);

  z_history_buffer_index = history->z_history_buffer_front_index;

  if (history->z_history_buffer_back_index
       < history->z_history_buffer_front_index)
  {
    back_index = history->z_history_buffer_back_index;
    final_pass_active = true;
  }
  else
  {
    back_index = history->z_history_buffer;
    final_pass_active = false;
  }

  // First we'll try to find the requested paragraph.

  // In case the last char in the history is already a newline, we'll have
  // to adjust the paragraph number, otherwise the first paragraph will be
  // empty.
  if (*z_history_buffer_index == Z_UCS_NEWLINE)
    paragraph_number++;

  while (
      (z_history_buffer_index != history->z_history_buffer_back_index - 1)
      || 
      (final_pass_active == false)
      )
  {
    while (z_history_buffer_index >= back_index)
    {
      if (*z_history_buffer_index == Z_UCS_NEWLINE)
      {
        paragraph_number--;

        TRACE_LOG("Paragraph: %d (%p).\n", paragraph_number,
            z_history_buffer_index);

        if (paragraph_number == 0)
          break;

        else if (paragraph_to_find - paragraph_number
            > history->pos_cache_max_paragraph)
          store_paragraph_pos_in_cache(
              history,
             paragraph_to_find - paragraph_number,
             z_history_buffer_index);
      }
      z_history_buffer_index--;
    }

    if (paragraph_number == 0)
      break;

    if (z_history_buffer_index == history->z_history_buffer - 1)
    {
      if (final_pass_active == true)
        break;
      z_history_buffer_index = history->z_history_buffer_last_element;
      back_index = history->z_history_buffer_back_index;
      final_pass_active = true;
    }
  }

  TRACE_LOG("Search ended at %p (backindex: %p).\n",
      z_history_buffer_index, back_index);

  if (paragraph_number == 0)
  {
    // Skip newline.
    if (z_history_buffer_index == history->z_history_buffer_last_element)
    {
      // Avoid skipping the very latest char in the buffer.
      if (history->z_history_buffer_front_index
          == history->z_history_buffer_last_element)
        return NULL;
      else
        z_history_output_start = history->z_history_buffer;
    }
    else
    {
      if (z_history_buffer_index == history->z_history_buffer_front_index)
        return NULL;
      else
        z_history_output_start = z_history_buffer_index + 1;
    }

    TRACE_LOG("Found paragraph in question at %p.\n",
        /*@-usedef@*/ z_history_output_start);
  }
  else
  {
    if (paragraph_number == 1)
    {
      store_paragraph_pos_in_cache(
          history, paragraph_to_find, history->z_history_buffer_back_index);
      return history->z_history_buffer_back_index;
    }
    else
    {
      TRACE_LOG("Paragraph not found.\n");
      return NULL;
    }
  }


  /*@-onlytrans@*/
  store_paragraph_pos_in_cache(history, paragraph_to_find,
      z_history_output_start);

  TRACE_LOG("result:%p\n", z_history_output_start);
  return z_history_output_start;
  /*@+onlytrans@*/
}


#ifdef ENABLE_TRACING
static void dumpHistoryBuffer(OUTPUTHISTORY *history)
{
  z_ucs buf;

    TRACE_LOG("Buffer contents:\n");

  if (history->z_history_buffer_front_index
      < history->z_history_buffer_back_index)
  {
    // wrap-around situation
    history->z_history_buffer_last_element[1] = 0;

    TRACE_LOG("---(%p)---\n", history->z_history_buffer_back_index);
    TRACE_LOG_Z_UCS(history->z_history_buffer_back_index);

    TRACE_LOG("\n---(%p)\n", history->z_history_buffer);

    buf = history->z_history_buffer_front_index[1];
    history->z_history_buffer_front_index[1] = 0;
    TRACE_LOG_Z_UCS(history->z_history_buffer);
    history->z_history_buffer_front_index[1] = buf;

    TRACE_LOG("\n---\n");
  }
  else
  {
    TRACE_LOG("---(%p)---\n", history->z_history_buffer);
    buf = history->z_history_buffer_front_index[1];
    history->z_history_buffer_front_index[1] = 0;
    TRACE_LOG_Z_UCS(history->z_history_buffer_back_index);
    history->z_history_buffer_front_index[1] = buf;
    TRACE_LOG("\n---\n");
  }
}
#endif


void repeat_paragraphs_from_history(OUTPUTHISTORY *history,
    int start_paragraph_number, int end_paragraph_number,
    bool allow_adjust_start_paragraph,
    struct history_repetition_target *target)
{
  z_ucs *z_history_output_start;
  z_ucs *z_history_output_end;
  z_ucs *z_history_output_real_end;
  z_font output_start_font;
  z_style output_start_style;
  z_colour output_start_foreground_colour;
  z_colour output_start_background_colour;
  z_ucs buf;
  int metadata_index, output_start_metadata_index;
  z_ucs *metadata_position;
  bool inner_loop_done, outer_loop_done;
  bool style_found, font_found, colour_found;

#ifdef ENABLE_TRACING
  // Uncomment the lower line to debug history (or rather to fill up your disk):
  dumpHistoryBuffer(history);
#endif

  if (start_paragraph_number == 0)
    return;

  if (
      (start_paragraph_number > 0)
      &&
      (end_paragraph_number >= start_paragraph_number)
     )
    return;

  if (history->z_history_buffer_front_index == NULL)
    return;

  TRACE_LOG("Repeating interface output from history at paragraph %d.\n",
      start_paragraph_number);

  z_history_output_start
    = find_paragraphs_from_history(history, start_paragraph_number);
  TRACE_LOG("Start of output: %p\n", z_history_output_start);

  // In case the start paragraph was not found and adjusting the start
  // paragraph is not allowed, nothing is repeated.
  if (z_history_output_start == NULL)
  {
    if (bool_equal(allow_adjust_start_paragraph, false))
    {
      TRACE_LOG("Returning, allow_adjust_start_paragraph == false.\n");
      return;
    }

    // Otherwise, output starts from the beginning of the buffer.
    target->repeat_starts_from_buffer_begin();
    z_history_output_start = history->z_history_buffer_back_index;
  }

  z_history_output_real_end
    = end_paragraph_number < 1
    ? history->z_history_buffer_front_index
    : find_paragraphs_from_history(history, end_paragraph_number) - 1;

  TRACE_LOG("Start of output: %p, end of output: %p.\n",
      z_history_output_start, z_history_output_real_end);

  TRACE_LOG("Entries in meta-data-buffer: %d.\n",
      history->history_buffer_metadata_index);

  if (z_history_output_start == NULL)
    return;

  TRACE_LOG("Output-start is at %p, front-index: %p.\n",
      z_history_output_start,
      history->z_history_buffer_front_index);

  // At this point we know that we've either found the start of the
  // paragraph in question or that we're at the start of the buffer,
  // at the back-index.

  output_start_font
    = history->history_buffer_back_index_font;
  output_start_style
    = history->history_buffer_back_index_style;
  output_start_foreground_colour
    = history->history_buffer_back_index_foreground;
  output_start_background_colour
    = history->history_buffer_back_index_background;

  if (ver <= 3)
  {
    style_found = true;
    font_found = true;
    colour_found = true;
  }
  else
  {
    style_found = false;

    if (ver < 5)
    {
      font_found = true;
      colour_found = true;
    }
    else
    {
      font_found = false;
      colour_found = false;
    }
  }

  // Start metadata lookup at the bottom of the data-list.
  metadata_index
    = history->history_buffer_metadata_size == 0
    ? -1
    : history->history_buffer_metadata_index - 1;

  // Rewind index until we've found an index somewhere before the start
  // of the actual output.
  while (
      (metadata_index >= 0)
      &&
      (
       (history->history_metadata_buffer[metadata_index].history_index
        == NULL)
       ||
       (z_history_output_start <= z_history_output_real_end
        ? // no wrap-around
        (history->history_metadata_buffer[metadata_index].history_index
         >= z_history_output_start)
        : // wrap-around
        (
         (
          // between (S)tart of (o)utput and physical end of buffer.
          // [oooooE....FB...XSoo*oo]
          (history->history_metadata_buffer[metadata_index].history_index
           >= z_history_output_start)
          &&
          (history->history_metadata_buffer[metadata_index].history_index
           <= history->z_history_buffer_back_index)
         )
         ||
         (
          // between physical start of buffer and (E)nd of output.
          // [o*oooE....FB....Sooooo]
          (history->history_metadata_buffer[metadata_index].history_index
           >= history->z_history_buffer)
          &&
          (history->history_metadata_buffer[metadata_index].history_index
           <= history->z_history_buffer_back_index)
         )
        )
       )
      )
     )
  {
    TRACE_LOG("Skipping metadata-entry at %p.\n", 
        history->history_metadata_buffer[metadata_index].history_index);
    metadata_index--;

#ifdef ENABLE_TRACING
    if (metadata_index > 0)
    {
      TRACE_LOG("Next metadata-entry at %p.\n", 
          history->history_metadata_buffer[metadata_index].history_index);
    }
#endif
  }

#ifdef ENABLE_TRACING
  if (metadata_index >= 0)
    TRACE_LOG("Last metadata-index %d before start at %p.\n",
        metadata_index,
        history->history_metadata_buffer[metadata_index].history_index);
#endif

  output_start_metadata_index = metadata_index;

#ifdef ENABLE_TRACING
  if (metadata_index >= 0)
    TRACE_LOG("Examine metadata from %p.\n",
        history->history_metadata_buffer[metadata_index].history_index);
#endif

  // Now we'll try to find the last entry for style, font and colour
  // settings.
  while (
      (bool_equal(style_found, false))
      ||
      (bool_equal(font_found, false))
      ||
      (bool_equal(colour_found, false))
      )
  {
    if (metadata_index < 0)
      break;

    if (
        (history->history_metadata_buffer[metadata_index].metadata_type
         == HISTORY_METADATA_TYPE_FONT)
        &&
        (bool_equal(font_found, false))
       )
    {
      TRACE_LOG("Found font %d at %p.\n",
          history->history_metadata_buffer[metadata_index].parameter1,
          history->history_metadata_buffer[metadata_index].history_index);
      output_start_font
        = history->history_metadata_buffer[metadata_index].parameter1;
      font_found = true;
    }
    else if (
        (history->history_metadata_buffer[metadata_index].metadata_type
         == HISTORY_METADATA_TYPE_STYLE)
        &&
        (bool_equal(style_found, false))
        )
    {
      TRACE_LOG("Found style %d at %p.\n",
          history->history_metadata_buffer[metadata_index].parameter1,
          history->history_metadata_buffer[metadata_index].history_index);
      output_start_style
        = history->history_metadata_buffer[metadata_index].parameter1;
      style_found = true;
    }
    else if (
        (history->history_metadata_buffer[metadata_index].metadata_type
         == HISTORY_METADATA_TYPE_COLOUR)
        &&
        (bool_equal(colour_found, false))
        )
    {
      TRACE_LOG("Found colour %d,%d at %p.\n",
          history->history_metadata_buffer[metadata_index].parameter1,
          history->history_metadata_buffer[metadata_index].parameter2,
          history->history_metadata_buffer[metadata_index].history_index);
      output_start_foreground_colour
        = history->history_metadata_buffer[metadata_index].parameter1;
      output_start_background_colour
        = history->history_metadata_buffer[metadata_index].parameter2;
      colour_found = true;
    }

    metadata_index--;
  }

  if (bool_equal(style_found, false))
  {
    TRACE_LOG("Loading back index history value for style: %d.\n",
        history->history_buffer_back_index_style);
    output_start_style = history->history_buffer_back_index_style;
  }

  if (bool_equal(font_found, false))
  {
    TRACE_LOG("Loading back index history value for font: %d.\n",
        history->history_buffer_back_index_font);
    output_start_font = history->history_buffer_back_index_font;
  }

  if (bool_equal(colour_found, false))
  {
    TRACE_LOG("Loading back index history value for colour: %d, %d\n",
        history->history_buffer_back_index_foreground,
        history->history_buffer_back_index_background);
    output_start_foreground_colour
      = history->history_buffer_back_index_foreground;
    output_start_background_colour
      = history->history_buffer_back_index_background;
  }

  if (ver >= 4)
  {
    TRACE_LOG("History start style: %d.\n", output_start_style);
    target->set_text_style(output_start_style);
  }

  if (ver >= 5)
  {
    TRACE_LOG("History start font: %d.\n", output_start_font);
    target->set_font(output_start_font);

    TRACE_LOG("History start colour: %d, %d.\n",
        output_start_foreground_colour,
        output_start_background_colour);

    target->set_colour(
        output_start_foreground_colour,
        output_start_background_colour,
        (int16_t)history->window_number);
  }

  if ((metadata_index = output_start_metadata_index) == -1)
    metadata_index = 0;

  outer_loop_done = false;
  while (bool_equal(outer_loop_done, false))
  {
    if (z_history_output_real_end < z_history_output_start)
    {
      z_history_output_end = history->z_history_buffer_last_element;

      TRACE_LOG("Output from output_start %p to buffer-end %p.\n",
          z_history_output_start,
          history->z_history_buffer_last_element);
    }
    else
    {
      z_history_output_end = z_history_output_real_end;

      TRACE_LOG("Output from buffer_start %p to output-end %p.\n",
          z_history_output_start,
          z_history_output_end);
    }

    inner_loop_done = false;
    while (bool_equal(inner_loop_done, false))
    {
      TRACE_LOG("%p\n", z_history_output_start);
#ifdef ENABLE_TRACING
      TRACE_LOG("first char: %c\n", *z_history_output_start);
      if ((metadata_index >= 0) && (history->history_buffer_metadata_size > 0))
        TRACE_LOG("Next metadata %d at %p.\n",
            metadata_index,
            history->history_metadata_buffer[metadata_index].history_index);
#endif
      TRACE_LOG("%p\n", z_history_output_start);

      TRACE_LOG("#\n");

      while (
          // In case the variable "history_buffer_metadata_index" is greater 0,
          // "history_buffer_metadata_index" is always non-null.
          (metadata_index < history->history_buffer_metadata_index)
          &&
          (/*@-nullderef@*/history->history_metadata_buffer
           /*@+nullderef@*/[metadata_index].history_index
           <= z_history_output_end)
          &&
          (/*@-nullderef@*/history->history_metadata_buffer
           /*@+nullderef@*/[metadata_index].history_index != NULL)
          )
      {
        metadata_position
          = /*@-nullderef@*/history->history_metadata_buffer
          /*@+nullderef@*/[metadata_index].history_index;

        TRACE_LOG("Found metadata at buffer-index %p in entry %d.\n",
            metadata_position, metadata_index);

        if (z_history_output_start < metadata_position)
        {
          buf = *metadata_position;
          *metadata_position = 0;
          TRACE_LOG("Output between metadata from %p to %p.\n",
              z_history_output_start, metadata_position);
          /*@-nullderef@*/
          target->z_ucs_output(z_history_output_start);
          /*@+nullderef@*/
          *metadata_position = buf;
          z_history_output_start = metadata_position;
        }

        if (/*@-nullderef@*/history->history_metadata_buffer
            /*@+nullderef@*/[metadata_index].metadata_type
            == HISTORY_METADATA_TYPE_FONT)
        {
          TRACE_LOG("Setting interface font from historized metadata.\n");
          /*@-nullderef@*/
          target->set_font(
          /*@+nullderef@*/
              (z_font)/*@-nullderef@*/history->history_metadata_buffer
              /*@+nullderef@*/[metadata_index].parameter1);
        }
        else if (/*@-nullderef@*/history->history_metadata_buffer
            /*@+nullderef@*/[metadata_index].metadata_type
            == HISTORY_METADATA_TYPE_STYLE)
        {
          TRACE_LOG("Setting interface style from historized metadata.\n");
          /*@-nullderef@*/
          target->set_text_style(
          /*@+nullderef@*/
              (z_style)/*@-nullderef@*/history->history_metadata_buffer
              /*@+nullderef@*/[metadata_index].parameter1);
        }
        else if (/*@-nullderef@*/history->history_metadata_buffer
            /*@+nullderef@*/[metadata_index].metadata_type
            == HISTORY_METADATA_TYPE_COLOUR)
        {
          TRACE_LOG("Setting interface colour from historized metadata.\n");
          /*@-nullderef@*/
          target->set_colour(
          /*@+nullderef@*/
            (z_colour)/*@-nullderef@*/history->history_metadata_buffer
            /*@+nullderef@*/[metadata_index].parameter1,
            (z_colour)/*@-nullderef@*/history->history_metadata_buffer
            /*@+nullderef@*/[metadata_index].parameter2,
            (int16_t)history->window_number);
        }
        else
        {
          TRACE_LOG("Problem! Unknown metadata-type '%d'.\n", 
              /*@-nullderef@*/history->history_metadata_buffer
              /*@+nullderef@*/[metadata_index].metadata_type);
        }

        metadata_index++;
        TRACE_LOG("New metadata-index: %d.\n", metadata_index);

        TRACE_LOG("New metadata: Entry %d/%d at buffer index %p.\n",
            metadata_index,
            history->history_buffer_metadata_index,
            /*@-nullderef@*/history->history_metadata_buffer
            /*@+nullderef@*/[metadata_index].history_index);
      }

      if (z_history_output_start <= z_history_output_end)
      {
        buf = z_history_output_end[1];
        z_history_output_end[1] = 0;
        TRACE_LOG("Output after metadata-check from %p.\n",
            z_history_output_start);
        /*@-nullderef@*/
        target->z_ucs_output(z_history_output_start);
        /*@+nullderef@*/
        z_history_output_end[1] = buf;
        z_history_output_start = z_history_output_end;
      }

      if (z_history_output_start == z_history_output_end)
        inner_loop_done = true;
    }

    if (z_history_output_start == z_history_output_real_end)
      outer_loop_done = true;
    else if (z_history_output_start == history->z_history_buffer_last_element)
      z_history_output_start = history->z_history_buffer;
  }
}


/*@null@*/ z_ucs *get_current_line(/*@null@*/ OUTPUTHISTORY *history)
{
  z_ucs *back_index;
  z_ucs *front_index;
  z_font output_start_font;
  z_style output_start_style;
  z_colour output_start_foreground_colour;
  z_colour output_start_background_colour;
  size_t length;
  size_t result_length;
  z_ucs *result;

  if (history == NULL)
    return NULL;

  if (history->z_history_buffer_front_index == NULL)
    return NULL;

  back_index = find_paragraphs_from_history(history, 1);

  if (back_index == NULL)
  {
    TRACE_LOG("Cannot find anything to output for last line.\n");
    return NULL;
  }

  TRACE_LOG("Output-start is at %p, front-index: %p.\n",
      back_index,
      history->z_history_buffer_front_index);

  // Here we know that we've either found the start of the paragraph
  // in question or that we're at the start of the buffer, at the
  // back-index.

  output_start_font
    = history->history_buffer_back_index_font;

  output_start_style
    = history->history_buffer_back_index_style;

  output_start_foreground_colour
    = history->history_buffer_back_index_foreground;

  output_start_background_colour
    = history->history_buffer_back_index_background;

  // We'll now iterate from the start of the buffer to the found
  // position and apply all style-changes to get the correct style
  // setting at the output-start.

  // FIXME: Implement.

  front_index
    = history->z_history_buffer_front_index > back_index
    ? history->z_history_buffer_front_index
    : history->z_history_buffer_last_element;

  if (history->z_history_buffer_front_index >= back_index)
  {
    length = (size_t)(history->z_history_buffer_front_index - back_index + 1);
  }
  else
  {
    length
      = (size_t)(
          (history->z_history_buffer_last_element - back_index + 1)
          + (history->z_history_buffer_front_index
            - history->z_history_buffer
            + 1));
  }

  result_length = (size_t)(length + 1) * sizeof(z_ucs);

  TRACE_LOG("Allocating %ld bytes for current-line buffer.\n", result_length);
  result = (z_ucs*)fizmo_malloc(result_length);
  TRACE_LOG("result-ptr: %p\n", result);

  if (history->z_history_buffer_front_index >= back_index)
  {
    z_ucs_ncpy(result, back_index, length);
    result[length] = 0;
  }
  else
  {
    z_ucs_ncpy(
        result,
        back_index,
        (size_t)(history->z_history_buffer_last_element
          - back_index + 1));

    z_ucs_ncpy(
        result + (history->z_history_buffer_last_element - back_index + 1),
        history->z_history_buffer,
        (size_t)(history->z_history_buffer_front_index
          - history->z_history_buffer + 1));

    result[length] = 0;
  }

  TRACE_LOG("Current line contents: '");
  TRACE_LOG_Z_UCS(result);
  TRACE_LOG("'.\n");

  return result;
}


void init_scrollback_count_wordwrapper(int screen_width, int left_padding)
{
  if (scrollback_count_wordwrapper == NULL)
    scrollback_count_wordwrapper = wordwrap_new_wrapper(
        screen_width,
        &history_wrapper_output_target,
        (void*)NULL,
        left_padding,
        true);
  else
    wordwrap_adjust_line_length(scrollback_count_wordwrapper, scrollback_width);
}


// Stores the paragraph whose contents are in lines top_y / bottom_y in
// the *_pos variables. Returns the highest y-pos processed last (and such
// gives the highest possible y-pos for the current buffer when top_y
// could not be found).
int get_paragraph_y_positions(OUTPUTHISTORY *history, int screen_width,
    int bottom_y, int *bottom_y_pos, int top_y, int *top_y_pos,
    int left_padding)
{
  int i = 0;
  int last_y = 0;

  if (screen_width < 1)
    return -1;

  if ( (top_y <= 0) && (bottom_y <= 0) )
    return -1;

  if ( (top_y > 0) && (top_y_pos == NULL) )
    return -1;

  if (bottom_y >= 0)
  {
    if (bottom_y_pos == NULL)
      return -1;
    *bottom_y_pos = -1;
  }

  TRACE_LOG("screen width: %d.\n", screen_width);

  scrollback_width = screen_width;
  init_scrollback_count_wordwrapper(screen_width, left_padding);

  do
  {
    scrollback_counter_lines = 0;
    scrollback_counter_chars = 0;

    TRACE_LOG("Starting repeat on paragraph %d.\n", i);

    wordwrap_set_line_index(scrollback_count_wordwrapper, 0);

    wordwrap_output_left_padding(scrollback_count_wordwrapper);
    repeat_paragraphs_from_history(
        history, i+1, i, false, &repetition_counting);

    wordwrap_flush_output(scrollback_count_wordwrapper);

    if (scrollback_counter_chars
        == scrollback_count_wordwrapper->left_side_padding)
      break;

    last_y += scrollback_counter_lines;

    if  (
        (bottom_y >= 0)
        &&
        (last_y >= bottom_y)
        &&
        (bottom_y_pos != NULL)
        &&
        (*bottom_y_pos < 0)
        )
      *bottom_y_pos = i-1;

    TRACE_LOG("Last paragraph %d had %d chars.\n", i, scrollback_counter_chars);
    TRACE_LOG("Last paragraph %d had %d lines.\n", i, scrollback_counter_lines);
    TRACE_LOG("Current y: %d.\n", last_y);

    i++;
  }
  while (
      (last_y < top_y)
      ||
      ( (top_y == -1) && (last_y < bottom_y) )
      );

  if (top_y != -1)
  {
    if (scrollback_counter_chars
        == scrollback_count_wordwrapper->left_side_padding)
      *top_y_pos = -1;
    else
      *top_y_pos = i;
  }

  return last_y;
}


void remove_chars_from_history(OUTPUTHISTORY *history, int nof_chars)
{
  if (history->z_history_buffer_front_index == NULL)
    return;

  if (history->z_history_buffer_front_index
      >= history->z_history_buffer_back_index)
  {
    if (history->z_history_buffer_front_index
        - history->z_history_buffer_back_index >= nof_chars)
      history->z_history_buffer_front_index -= nof_chars;
    else
      history->z_history_buffer_front_index
        = history->z_history_buffer_back_index;
  }
  else
  {
    if (history->z_history_buffer_front_index - nof_chars
        >= history->z_history_buffer)
    {
      history->z_history_buffer_front_index -= nof_chars;
    }
    else
    {
      nof_chars
        -= history->z_history_buffer_front_index - history->z_history_buffer;

      history->z_history_buffer_front_index
        = history->z_history_buffer_last_element;

      if (history->z_history_buffer_front_index
          - history->z_history_buffer_back_index >= nof_chars)
        history->z_history_buffer_front_index -= nof_chars;
      else
        history->z_history_buffer_front_index
          = history->z_history_buffer_back_index;
    }
  }
}

#endif // history_c_INCLUDED

