/* undo - primitives for implementing undo
**
** Copyright (C) 2022 by Jef Poskanzer <jef@mail.acme.com>.
** All rights reserved.
**
** You may use this software however you like as long as you keep my
** name on it and don't sue me.
**
** For commentary on this license please see http://acme.com/license.html
*/

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

#include "undo.h"
#include "str.h"
#include "leakfinder.h"


/* Undo record. */


undo_record* undo_record_create( void )
    {
    undo_record* ur;

    ur = (undo_record*) MALLOC( sizeof(undo_record) );
    if ( ur == (undo_record*) 0 )
	return (undo_record*) 0;
    ur->s = str_create();;
    if ( ur->s == (str*) 0 )
	{
	FREE( ur );
	return (undo_record*) 0;
	}
    ur->loc = -1;
    ur->delta = 0;
    ur->len = 0;
    ur->point = ur->mark = 0;
    ur->modified = false;
    return ur;
    }


void undo_record_destroy( undo_record* ur )
    {
    str_destroy( ur->s );
    FREE( ur );
    }


/* Undo record stack. */

struct undo_stack_entry
    {
    undo_record* ur;
    SLIST_ENTRY( undo_stack_entry ) entries;
    };


undo_stack* undo_stack_create( void )
    {
    undo_stack* us = MALLOC( sizeof(undo_stack) );
    if ( us == (undo_stack*) 0 )
	return (undo_stack*) 0;
    SLIST_INIT( us );
    return us;
    }


bool undo_stack_empty( undo_stack* us )
    {
    return SLIST_EMPTY( us );
    }


bool undo_stack_push( undo_stack* us, undo_record* ur )
    {
    struct undo_stack_entry* e = MALLOC( sizeof(struct undo_stack_entry) );
    if ( e == (struct undo_stack_entry*) 0 )
	return false;
    e->ur = ur;
    SLIST_INSERT_HEAD( us, e, entries );
    return true;
    }


undo_record* undo_stack_top( undo_stack* us )
    {
    struct undo_stack_entry* e;
    undo_record* ur;

    e = SLIST_FIRST( us );
    ur = e->ur;
    return ur;
    }


undo_record* undo_stack_pop( undo_stack* us )
    {
    struct undo_stack_entry* e;
    undo_record* ur;

    e = SLIST_FIRST( us );
    SLIST_REMOVE_HEAD( us, entries );
    ur = e->ur;
    FREE( (void*) e );
    return ur;
    }


void undo_stack_clear( undo_stack* us )
    {
    undo_record* ur;

    while ( ! undo_stack_empty( us ) )
	{
	ur = undo_stack_pop( us );
	undo_record_destroy( ur );
	}
    }


void undo_stack_destroy( undo_stack* us )
    {
    undo_stack_clear( us );
    FREE( (void*) us );
    }
