/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * See the COPYING file for license information.
 *
 * Guillaume Chazarain <guichaz@gmail.com>
 */

/********************
 * History handling *
 ********************/

#include "gliv.h"
#include "history.h"
#include "options.h"
#include "rendering.h"
#include "matrix.h"

extern options_struct *options;

static GList *list = NULL;
static GList *node = NULL;      /* Currrent position in the list. */

static gint count = 0;          /* Number of elements in the list. */

/* Returns item->next. */
static GList *free_item(GList * item)
{
    GList *next;

    g_free(item->data);
    next = item->next;
    count--;

    list = g_list_delete_link(list, item);
    return next;
}

/* Free the elements from there to the end. */
static void free_list(GList * item)
{
    while (item != NULL)
        item = free_item(item);
}

/* Keep the list from being larger than options->history_size. */
void clean_history(void)
{
    if (options->history_size < 0)
        /* Unlimited history. */
        return;

    while (count > options->history_size) {
        if (node == list)
            node = node->next;
        list = free_item(list);
    }

    if (list != NULL)
        list->prev = NULL;
}

/* Add the current configuration to the end of the list. */
void append_history(void)
{
    GList *item;

    if (options->history_size == 0)
        /* No history. */
        return;

    item = g_list_alloc();

    /* Fill the data. */
    item->data = new_matrix();
    matrix_cpy(item->data, NULL);

    /* We are at the end of the list. */
    item->prev = node;
    item->next = NULL;

    if (list == NULL)
        /* This is the first element. */
        list = item;
    else {
        if (node->next != NULL)
            /* We discard what was after us. */
            free_list(node->next);

        node->next = item;
    }

    node = item;
    count++;
    clean_history();
}

static void set_state(GList * item)
{
    if (item != NULL && item->data != NULL) {
        node = item;
        matrix_cpy(NULL, item->data);
        refresh(REFRESH_IMAGE | REFRESH_STATUS);
    }
}

gboolean undo(void)
{
    set_state(g_list_previous(node));
    return FALSE;
}

gboolean redo(void)
{
    set_state(g_list_next(node));
    return FALSE;
}

/* Restart from scratch. */
gboolean clear_history(void)
{
    if (list != NULL) {
        free_list(list);
        list = node = NULL;
        append_history();
    }

    return FALSE;
}
