/********************************************************************\
 * gnc-dense-cal-store.h                                            *
 * Copyright (C) 2006 Joshua Sled <jsled@asynchronous.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, under version 2 and   *
 *  / or version 3 of the License.                                  *
 *                                                                  *
 * As a special exception, permission is granted to link the binary *
 * module resultant from this code with the OpenSSL project's       *
 * "OpenSSL" library (or modified versions of it that use the same  *
 * license as the "OpenSSL" library), and distribute the linked     *
 * executable. You must obey the GNU General Public License in all  *
 * respects for all of the code used other than "OpenSSL". If you   *
 * modify this file, you may extend this exception to your version  *
 * of the file, but you are not obligated to do so. If you do not   *
 * wish to do so, delete this exception statement from your version *
 * of this file.                                                    *
 *                                                                  *
 * 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, contact:                        *
 *                                                                  *
 * Free Software Foundation           Voice:  +1-617-542-5942       *
 * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
 * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
\********************************************************************/

#include <config.h>
#include <glib.h>
#include <glib-object.h>
#include "gnc-date.h"
#include "gnc-dense-cal.h"
#include "gnc-dense-cal-model.h"
#include "gnc-dense-cal-store.h"
#include "Recurrence.h"

struct _GncDenseCalStore
{
    GObject parent;

    GDate start_date;
    gdcs_end_type end_type;
    GDate end_date;
    gint n_occurrences;
    gchar *name;
    gchar *info;
    int num_marks;
    int num_real_marks;
    GDate **cal_marks;
};

static void gnc_dense_cal_store_iface_init (GncDenseCalModelInterface *iface);
static void gnc_dense_cal_store_finalize (GObject *obj);

static GList* gdcs_get_contained (GncDenseCalModel *model);
static gchar* gdcs_get_name (GncDenseCalModel *model, guint tag);
static gchar* gdcs_get_info (GncDenseCalModel *model, guint tag);
static gint gdcs_get_instance_count (GncDenseCalModel *model, guint tag);
static void gdcs_get_instance (GncDenseCalModel *model, guint tag, gint instance_index, GDate *date);

G_DEFINE_TYPE_WITH_CODE(GncDenseCalStore, gnc_dense_cal_store, G_TYPE_OBJECT,
    G_IMPLEMENT_INTERFACE(GNC_TYPE_DENSE_CAL_MODEL, gnc_dense_cal_store_iface_init))

static void
gnc_dense_cal_store_class_init (GncDenseCalStoreClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS(klass);

    object_class->finalize = gnc_dense_cal_store_finalize;
}

static void
gnc_dense_cal_store_init (GncDenseCalStore *self)
{
}

static void
gnc_dense_cal_store_iface_init (GncDenseCalModelInterface *iface)
{
    iface->get_contained = gdcs_get_contained;
    iface->get_name = gdcs_get_name;
    iface->get_info = gdcs_get_info;
    iface->get_instance_count = gdcs_get_instance_count;
    iface->get_instance = gdcs_get_instance;
}

GncDenseCalStore*
gnc_dense_cal_store_new (int num_marks)
{
    GncDenseCalStore *model = g_object_new (GNC_TYPE_DENSE_CAL_STORE, NULL);
    model->num_marks = num_marks;
    model->cal_marks = g_new0 (GDate*, num_marks);
    {
        for (int i = 0; i < model->num_marks; i++)
        {
            model->cal_marks[i] = g_date_new();
        }
    }
    model->num_real_marks = 0;
    g_date_clear (&model->start_date, 1);
    gnc_gdate_set_today (&model->start_date);
    model->end_type = NEVER_END;
    g_date_clear (&model->end_date, 1);
    gnc_gdate_set_today (&model->end_date);
    model->n_occurrences = 0;
    return model;
}

void
gnc_dense_cal_store_clear (GncDenseCalStore *model)
{
    model->num_real_marks = 0;
    g_signal_emit_by_name (model, "update", GUINT_TO_POINTER(1));
}

void
gnc_dense_cal_store_update_name (GncDenseCalStore *model, const gchar *name)
{
    if (model->name != NULL)
        g_free (model->name);

    model->name = g_strdup (name);
    //g_signal_emit_by_name(model, "update", GUINT_TO_POINTER(1));
}

void
gnc_dense_cal_store_update_info (GncDenseCalStore *model, const gchar *info)
{
    if (model->info != NULL)
        g_free (model->info);

    model->info = g_strdup (info);
    //g_signal_emit_by_name(model, "update", GUINT_TO_POINTER(1));
}

static void
gdcs_generic_update_recurrences (GncDenseCalStore *trans, GDate *start, GList *recurrences)
{
    int i = 0;
    GDate date, next;

    date = *start;
    recurrenceListNextInstance (recurrences, &date, &next);

    while ((i < trans->num_marks)
            && g_date_valid (&next)
            /* Do checking against end restriction. */
            && ((trans->end_type == NEVER_END)
                || (trans->end_type == END_ON_DATE
                    && g_date_compare (&next, &trans->end_date) <= 0)
                || (trans->end_type == END_AFTER_N_OCCS
                    && i < trans->n_occurrences)))
    {
        *trans->cal_marks[i++] = next;
        date = next;
        recurrenceListNextInstance (recurrences, &date, &next);
    }
    trans->num_real_marks = i;
    /* cstim: Previously this was i-1 but that's just plain wrong for
     * occurrences which are coming to an end, because then i contains
     * the number of (rest) occurrences exactly! Subtracting one means
     * we will miss the last one. */

    g_signal_emit_by_name (trans, "update", GUINT_TO_POINTER(1));
}

void
gnc_dense_cal_store_update_recurrences_no_end (GncDenseCalStore *model,
                                               GDate *start,
                                               GList *recurrences)
{
    model->end_type = NEVER_END;
    gdcs_generic_update_recurrences (model, start, recurrences);
}

void
gnc_dense_cal_store_update_recurrences_count_end (GncDenseCalStore *model,
                                                  GDate *start,
                                                  GList *recurrences,
                                                  int num_occur)
{
    model->end_type = END_AFTER_N_OCCS;
    model->n_occurrences = num_occur;
    gdcs_generic_update_recurrences (model, start, recurrences);
}

void
gnc_dense_cal_store_update_recurrences_date_end (GncDenseCalStore *model,
                                                 GDate *start,
                                                 GList *recurrences,
                                                 GDate *end_date)
{
    model->end_type = END_ON_DATE;
    model->end_date = *end_date;
    gdcs_generic_update_recurrences (model, start, recurrences);
}

static GList*
gdcs_get_contained (GncDenseCalModel *model)
{
    GList *rtn = NULL;
    rtn = g_list_append (rtn, GUINT_TO_POINTER(1));
    return rtn;
}

static gchar*
gdcs_get_name (GncDenseCalModel *model, guint tag)
{
    GncDenseCalStore *mdl = GNC_DENSE_CAL_STORE(model);
    // assert(tag == 1)
    return mdl->name;
}

static gchar*
gdcs_get_info (GncDenseCalModel *model, guint tag)
{
    GncDenseCalStore *mdl = GNC_DENSE_CAL_STORE(model);
    // assert(tag == 1)
    return g_strdup (mdl->info);
}

static gint
gdcs_get_instance_count (GncDenseCalModel *model, guint tag)
{
    GncDenseCalStore *mdl = GNC_DENSE_CAL_STORE(model);
    // assert(tag == 1)
    return mdl->num_real_marks;
}

static void
gdcs_get_instance (GncDenseCalModel *model, guint tag, gint instance_index, GDate *date)
{
    GncDenseCalStore *mdl = GNC_DENSE_CAL_STORE(model);
    // assert(tag == 1)
    // assert 0 < instance_index < model->num_marks;
    *date = *mdl->cal_marks[instance_index];
}

static void
gnc_dense_cal_store_finalize (GObject *obj)
{
    GncDenseCalStore *store;
    g_return_if_fail (obj != NULL);

    store = GNC_DENSE_CAL_STORE(obj);

    if (store->name != NULL)
    {
        g_free (store->name);
        store->name = NULL;
    }

    if (store->info != NULL)
    {
        g_free (store->info);
        store->info = NULL;
    }

    for (int i = 0; i < store->num_marks; i++)
    {
        g_free (store->cal_marks[i]);
        store->cal_marks[i] = NULL;
    }
    if (store->cal_marks != NULL)
    {
        g_free (store->cal_marks);
        store->cal_marks = NULL;
    }

    G_OBJECT_CLASS(gnc_dense_cal_store_parent_class)->finalize (obj);
}
