/*
 * SwamiguiPaste.c - Swami item paste object
 *
 * Swami
 * Copyright (C) 1999-2014 Element Green <element@elementsofsound.org>
 *
 * 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; version 2
 * of the License only.
 *
 * 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 or point your web browser to http://www.gnu.org.
 */
#include <stdio.h>
#include <string.h>

#include <libinstpatch/libinstpatch.h>
#include <libswami/libswami.h>

#include "SwamiguiPaste.h"
#include "SwamiguiRoot.h"	/* for state history */
#include "i18n.h"

static void swamigui_paste_class_init(SwamiguiPasteClass *klass);
static void swamigui_paste_init(SwamiguiPaste *paste);
static void swamigui_paste_finalize(GObject *obj);

static GObjectClass *parent_class = NULL;


GType
swamigui_paste_get_type(void)
{
    static GType obj_type = 0;

    if(!obj_type)
    {
        static const GTypeInfo obj_info =
        {
            sizeof(SwamiguiPasteClass), NULL, NULL,
            (GClassInitFunc) swamigui_paste_class_init, NULL, NULL,
            sizeof(SwamiguiPaste), 0,
            (GInstanceInitFunc) swamigui_paste_init
        };

        obj_type = g_type_register_static(G_TYPE_OBJECT, "SwamiguiPaste",
                                          &obj_info, 0);
    }

    return (obj_type);
}

static void
swamigui_paste_class_init(SwamiguiPasteClass *klass)
{
    GObjectClass *obj_class = G_OBJECT_CLASS(klass);

    parent_class = g_type_class_peek_parent(klass);

    obj_class->finalize = swamigui_paste_finalize;
}

static void
swamigui_paste_init(SwamiguiPaste *paste)
{
    paste->status = SWAMIGUI_PASTE_NORMAL;
    paste->item_hash =
        g_hash_table_new_full(NULL, NULL,
                              g_object_unref, /* key destroy func */
                              g_object_unref);	/* value destroy func */

    /* don't want paste objects to be saved for session */
    swami_object_clear_flags(G_OBJECT(paste), SWAMI_OBJECT_SAVE);
}

static void
swamigui_paste_finalize(GObject *obj)
{
    SwamiguiPaste *paste = SWAMIGUI_PASTE(obj);

    if(paste->curitem)		/* paste not finished? Then cancel it. */
    {
        paste->status = SWAMIGUI_PASTE_CANCEL;
        swamigui_paste_process(paste);
    }

    if(paste->dstitem)
    {
        g_object_unref(paste->dstitem);    /* -- unref dest item */
    }

    if(paste->srcitems)		/* -- unref source items */
    {
        g_list_foreach(paste->srcitems, (GFunc)g_object_unref, NULL);
        g_list_free(paste->srcitems);
    }

    g_hash_table_destroy(paste->item_hash);

    /* clear conflict items to unref them */
    swamigui_paste_set_conflict_items(paste, NULL, NULL);
}

/**
 * swamigui_paste_new:
 *
 * Create a new paste object.
 *
 * Returns: New paste object with a ref count of 1.
 */
SwamiguiPaste *
swamigui_paste_new(void)
{
    return (SWAMIGUI_PASTE(g_object_new(SWAMIGUI_TYPE_PASTE, NULL)));
}

/**
 * swamigui_paste_process:
 * @paste: Swami paste object
 *
 * Run the paste process. This function uses the IpatchPaste system to handle
 * paste operations. May need to be called multiple times to complete a paste
 * operation if decisions are required (conflict paste items, unhandled types,
 * or a cancel occurs). All paste status and parameters are stored in
 * the @paste object. This function also groups all state history actions
 * of the paste operation so it can be retracted if the paste is canceled.
 *
 * Returns: %TRUE if paste finished successfully, %FALSE if a decision is
 * required or an error occured.
 */
gboolean
swamigui_paste_process(SwamiguiPaste *paste)
{
//  IpatchItemIface *iface;
    gboolean retval = TRUE;

    g_return_val_if_fail(SWAMIGUI_IS_PASTE(paste), FALSE);
    g_return_val_if_fail(paste->dstitem != NULL, FALSE);
    g_return_val_if_fail(paste->srcitems != NULL, FALSE);
    g_return_val_if_fail(paste->status != SWAMIGUI_PASTE_ERROR, FALSE);

    /* skip unhandled types if requested */
    if(paste->status == SWAMIGUI_PASTE_UNHANDLED
            && paste->decision == SWAMIGUI_PASTE_SKIP && paste->curitem)
    {
        paste->curitem = g_list_next(paste->curitem);
        paste->decision = SWAMIGUI_PASTE_NO_DECISION;
    }

    paste->status = SWAMIGUI_PASTE_NORMAL;	/* set status to normal */
    paste->decision_mask = SWAMIGUI_PASTE_SKIP | SWAMIGUI_PASTE_CHANGED
                           | SWAMIGUI_PASTE_REPLACE;	/* set decision mask to all */

//  retval = ((*iface->paste)(paste->dstitem, paste));

    paste->decision = SWAMIGUI_PASTE_NO_DECISION; /* clear previous decision */

    return (retval);
}

/**
 * swamigui_paste_set_items:
 * @paste: Swami paste object
 * @dstitem: Destination item of paste operation
 * @srcitems: A list of source #IpatchItem objects
 *
 * A function to set the destination item and source item(s) of a
 * paste operation. This function can only be called once on a paste
 * object.
 */
void
swamigui_paste_set_items(SwamiguiPaste *paste, IpatchItem *dstitem,
                         GList *srcitems)
{
//  IpatchItemIface *dstitem_iface;

    g_return_if_fail(SWAMIGUI_IS_PASTE(paste));
    g_return_if_fail(IPATCH_IS_ITEM(dstitem));
    g_return_if_fail(srcitems != NULL);

    g_return_if_fail(paste->dstitem == NULL);
    g_return_if_fail(paste->srcitems == NULL);

    /* make sure we have a paste method for the destination item */
//  dstitem_iface = IPATCH_ITEM_GET_IFACE (dstitem);
//  g_return_if_fail (dstitem_iface->paste != NULL);

    g_object_ref(dstitem);	/* ++ ref destination item */
    paste->dstitem = dstitem;

    /* copy and reference source item list */
    paste->srcitems = g_list_copy(srcitems);
    g_list_foreach(paste->srcitems, (GFunc)g_object_ref, NULL);  /* ++ ref */
    paste->curitem = paste->srcitems; /* set current item to first source item */
}

/**
 * swamigui_paste_get_conflict_items:
 * @paste: Swami paste object
 * @src: Location to store a pointer to the source conflict
 *   item (item's refcount is incremented) or %NULL
 * @dest: Location to store a pointer to the conflict destination
 *   item (item's refcount is incremented) or %NULL
 *
 * Get conflict items from a @paste object (paste status should be
 * #SWAMIGUI_PASTE_CONFLICT or these will be %NULL). The returned
 * items' reference counts are incremented and the caller is
 * responsible for unrefing them with g_object_unref when finished using
 * them.
 */
void
swamigui_paste_get_conflict_items(SwamiguiPaste *paste, IpatchItem **src,
                                  IpatchItem **dest)
{
    g_return_if_fail(SWAMIGUI_IS_PASTE(paste));

    if(src)
    {
        /* ++ ref returned src item */
        if(paste->conflict_src)
        {
            g_object_ref(paste->conflict_src);
        }

        *src = paste->conflict_src;
    }

    if(dest)
    {
        /* ++ ref returned dest item */
        if(paste->conflict_dst)
        {
            g_object_ref(paste->conflict_dst);
        }

        *dest = paste->conflict_dst;
    }
}

/**
 * swamigui_paste_set_conflict_items:
 * @paste: Swami paste object
 * @src: Source conflict item or %NULL
 * @dest: Destination conflict item or %NULL
 *
 * Set conflict items in a paste object. This function will cause the paste
 * object's status to be set to #SWAMIGUI_PASTE_CONFLICT if @src and
 * @dest are not %NULL, otherwise it will be set to
 * #SWAMIGUI_PASTE_NORMAL status and the conflict items will be cleared.
 */
void
swamigui_paste_set_conflict_items(SwamiguiPaste *paste, IpatchItem *src,
                                  IpatchItem *dest)
{
    g_return_if_fail(SWAMIGUI_IS_PASTE(paste));
    g_return_if_fail(!src || IPATCH_IS_ITEM(src));
    g_return_if_fail(!dest || IPATCH_IS_ITEM(dest));

    /* unref old items if set */
    if(paste->conflict_src)
    {
        g_object_unref(paste->conflict_src);
    }

    if(paste->conflict_dst)
    {
        g_object_unref(paste->conflict_dst);
    }

    if(src && dest)
    {
        paste->status = SWAMIGUI_PASTE_CONFLICT;
        g_object_ref(src);	/* ++ ref new src conflict item */
        g_object_ref(dest);	/* ++ ref new dest conflict item */
    }
    else
    {
        paste->status = SWAMIGUI_PASTE_NORMAL;
    }

    paste->conflict_src = src;
    paste->conflict_dst = dest;
}

/**
 * swamigui_paste_push_state:
 * @paste: Swami paste object
 * @state: #IpatchItem "paste" method defined state data
 *
 * This function is used by #IpatchItem interface "paste" methods to
 * store variable state data to be able to resume a paste operation (after
 * a decision is made on a conflict, etc). The @paste object stores a stack
 * of state data so a chain of functions can be resumed at a later time.
 * The methods are responsible for creating and destroying this state data
 * and the function chain will be resumed even on a cancel operation so this
 * can be accomplished properly.
 */
void
swamigui_paste_push_state(SwamiguiPaste *paste, gpointer state)
{
    g_return_if_fail(SWAMIGUI_IS_PASTE(paste));
    g_return_if_fail(state != NULL);

    paste->states = g_list_prepend(paste->states, state);
}

/**
 * swamigui_paste_pop_state:
 * @paste: Swami paste object
 *
 * This function is used by #IpatchItem interface "paste" methods to
 * retrieve the next variable state data, stored by
 * swamigui_paste_push_state(), to be able to resume a paste
 * operation. The chain of functions storing state data should call
 * this function in the reverse order in which the states were
 * pushed. The state pointer is removed from the stack after this
 * call.
 *
 * Returns: Pointer to state data which was on the top of the stack.
 */
gpointer
swamigui_paste_pop_state(SwamiguiPaste *paste)
{
    gpointer state;

    g_return_val_if_fail(SWAMIGUI_IS_PASTE(paste), NULL);
    g_return_val_if_fail(paste->states != NULL, NULL);

    state = paste->states->data;
    paste->states = g_list_delete_link(paste->states, paste->states);

    return (state);
}
