/*
 * gnc-sx-instance-model.h
 *
 * Copyright (C) 2006 Josh Sled <jsled@asynchronous.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 and/or version 3 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * 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
 */

/** \file
 */

#ifndef _GNC_SX_INSTANCE_MODEL_H
#define _GNC_SX_INSTANCE_MODEL_H

#include "config.h"
#include <glib.h>
#include <glib-object.h>
#include "gnc-numeric.h"
#include "SchedXaction.h"

G_BEGIN_DECLS

#define GNC_TYPE_SX_INSTANCE_MODEL	      (gnc_sx_instance_model_get_type ())
#define GNC_SX_INSTANCE_MODEL(obj)	      (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_SX_INSTANCE_MODEL, GncSxInstanceModel))
#define GNC_SX_INSTANCE_MODEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_SX_INSTANCE_MODEL, GncSxInstanceModelClass))
#define GNC_IS_SX_INSTANCE_MODEL(obj)	      (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_SX_INSTANCE_MODEL))
#define GNC_IS_SX_INSTANCE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_SX_INSTANCE_MODEL))
#define GNC_SX_INSTANCE_MODEL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_SX_INSTANCE_MODEL, GncSxInstanceModelClass))

typedef struct _GncSxInstanceModel
{
    GObject parent;
    gboolean disposed;

    /* private */
    gint qof_event_handler_id;

    /* signals */
    /* void (*added)(SchedXaction *sx); // gpointer user_data */
    /* void (*updated)(SchedXaction *sx); // gpointer user_data */
    /* void (*removing)(SchedXaction *sx); // gpointer user_data */

    /* public */
    GDate range_end;
    gboolean include_disabled;
    GList *sx_instance_list; /* <GncSxInstances*> */
} GncSxInstanceModel;

typedef struct _GncSxInstanceModelClass
{
    GObjectClass parent;

    guint removing_signal_id;
    guint updated_signal_id;
    guint added_signal_id;
} GncSxInstanceModelClass;

typedef struct _GncSxInstances
{
    SchedXaction *sx;
    GHashTable /** <name:char*,GncSxVariable*> **/ *variable_names;
    gboolean variable_names_parsed;

    GDate next_instance_date;

    /** GList<GncSxInstance*> **/
    GList *instance_list;
} GncSxInstances;

typedef enum
{
    SX_INSTANCE_STATE_IGNORED,
    SX_INSTANCE_STATE_POSTPONED,
    SX_INSTANCE_STATE_TO_CREATE,
    SX_INSTANCE_STATE_REMINDER,
    SX_INSTANCE_STATE_CREATED,
    SX_INSTANCE_STATE_MAX_STATE
} GncSxInstanceState;

typedef struct _GncSxVariable
{
    gchar *name;
    gnc_numeric value; /**< only numeric values are supported. **/
    gboolean editable;
} GncSxVariable;

typedef struct _GncSxInstance
{
    GncSxInstances *parent; /**< the parent instances collection. **/
    void *temporal_state; /**< the sx creation temporal state. **/
    GncSxInstanceState orig_state; /**< the original state at generation time. **/
    GncSxInstanceState state; /**< the current state of the instance (during editing) **/
    GDate date; /**< the instance date. **/
    GHashTable *variable_bindings; /**< variable bindings. **/
} GncSxInstance;

typedef struct _GncSxVariableNeeded
{
    GncSxInstance *instance;
    GncSxVariable *variable;
} GncSxVariableNeeded;

GType gnc_sx_instance_model_get_type(void);

/** Shorthand for get_instances(now, FALSE); */
GncSxInstanceModel* gnc_sx_get_current_instances(void);

/** Allocates a new SxInstanceModel and fills it with generated
 * instances for all scheduled transactions up to the given range_end
 * date.
 *
 * The caller must unref the returned object by
 * g_object_unref(G_OBJECT(inst_model)); when no longer in use. */
GncSxInstanceModel* gnc_sx_get_instances(const GDate *range_end, gboolean include_disabled);

/**
 * Regenerates and updates the GncSxInstances* for the given SX.  Model
 * consumers are probably going to call this in response to seeing the
 * "update" signal, unless they need to be doing something else like
 * finishing an iteration over an existing GncSxInstances*.
 **/
void gnc_sx_instance_model_update_sx_instances(GncSxInstanceModel *model, SchedXaction *sx);
void gnc_sx_instance_model_remove_sx_instances(GncSxInstanceModel *model, SchedXaction *sx);

/** @return GList<GncSxVariable*>. Caller owns the list, but not the items. **/
GList *gnc_sx_instance_get_variables(GncSxInstance *inst);

Account* gnc_sx_get_template_transaction_account(const SchedXaction *sx);

/**
 * @return caller-owned data struct.
 **/
GHashTable* gnc_sx_instance_get_variables_for_parser(GHashTable *instance_var_hash);

GncSxVariable* gnc_sx_variable_new_full(gchar *name, gnc_numeric value, gboolean editable);
void gnc_sx_variable_free(GncSxVariable *var);

/**
 * There is a constraint around a sequence of upcoming instance states.  In
 * short: the last-created state and a list of postponed instances are modeled,
 * but upcoming reminders are not.  As such, a reminder can never be before any
 * other (modeled) instance type.  For instance, the following sequences are
 * disallowed:
 *
 * [...]
 * remind    <- will be lost/skipped over; must be converted to `postponed`.
 * to-create <- this will be the last-recorded state.
 * [...]
 *
 * [...]
 * remind    <- same as previous; will be lost/skipped; must be `postponed`.
 * postponed
 * [...]
 *
 * remind    <- same...
 * ignore
 * [...]
 *
 *
 * As such, the SinceLastRun model will enforce that there are no previous
 * `remind` instances at every state change.  They will be silently converted to
 * `postponed`-state transactions.
 **/
void gnc_sx_instance_model_change_instance_state(GncSxInstanceModel *model,
        GncSxInstance *instance,
        GncSxInstanceState new_state);

void gnc_sx_instance_model_set_variable(GncSxInstanceModel *model,
                                        GncSxInstance *instance,
                                        GncSxVariable *variable,
                                        gnc_numeric *new_value);

/**
 * @return List<GncSxVariableNeeded> of unbound {instance,variable} pairs;
 * the caller owns the list and the items.
 **/
GList* gnc_sx_instance_model_check_variables(GncSxInstanceModel *model);

/** Really ("effectively") create the transactions from the SX
 * instances in the given model. */
void gnc_sx_instance_model_effect_change(GncSxInstanceModel *model,
        gboolean auto_create_only,
        GList **created_transaction_guids,
        GList **creation_errors);

typedef struct _GncSxSummary
{
    gboolean need_dialog; /**< If the dialog needs to be displayed. **/

    gint num_instances; /**< The number of total instances (in any state). **/
    gint num_to_create_instances; /**< The number of (not-auto-create) to-create instances. **/
    gint num_auto_create_instances;  /**< The total number of auto-create instances. **/
    gint num_auto_create_no_notify_instances; /**< The number of automatically-created instances that do no request notification. **/
} GncSxSummary;

/**
 * @param summary Caller-provided, populated with a summarization of the
 * state of the model.  Specifically, used to determine if there are SLR SXes
 * that need either auto-creation or user-interaction.
 **/
void gnc_sx_instance_model_summarize(GncSxInstanceModel *model, GncSxSummary *summary);

/** Debug output to trace file */
void gnc_sx_summary_print(const GncSxSummary *summary);

void gnc_sx_get_variables(SchedXaction *sx, GHashTable *var_hash);
int gnc_sx_parse_vars_from_formula(const char *formula, GHashTable *var_hash, gnc_numeric *result);
void gnc_sx_randomize_variables(GHashTable *vars);

/** Returns a GHashTable<GUID*, gnc_numeric*> with no destructor for
 * the key, but a destructor for the value set.
 *
 * The returned value must be free'd with g_hash_table_destroy or
 * g_hash_table_unref. */
GHashTable* gnc_g_hash_new_guid_numeric(void);

/** Calculates the cash flow of one single instance of the given SX
 * into the GHashTable<GUID*, gnc_numeric*> such that the cash flow on
 * each account is added to the given hash under the GUID of that
 * account.
 *
 * Exactly one instance of the SX is calculated, regardless of its
 * start date, recurrence, or end date.
 *
 * The creation_errors list, if non-NULL, receive any errors that
 * occurred during creation, similar as in
 * gnc_sx_instance_model_effect_change(). */
void gnc_sx_instantiate_cashflow(const SchedXaction* sx,
                                 GHashTable* map, GList **creation_errors);

/** Instantiates the cash flow of all given SXs (in the given
 * GList<SchedXAction*>) into the GHashTable<GUID*, gnc_numeric*> for the
 * given date range. Each SX is counted with multiplicity as it has
 * occurrences in the given date range.
 *
 * The creation_errors list, if non-NULL, receive any errors that
 * occurred during creation, similar as in
 * gnc_sx_instance_model_effect_change(). */
void gnc_sx_all_instantiate_cashflow(GList *all_sxes,
                                     const GDate *range_start, const GDate *range_end,
                                     GHashTable* map, GList **creation_errors);

/** Simplified wrapper around gnc_sx_all_instantiate_cashflow(): Run
 * that function on all SX of the current book for the given date
 * range. Ignore any potential error messages. Returns a newly
 * allocated GHashTable with the result, which is a GHashTable<GUID*,
 * gnc_numeric*>, identical to what gnc_g_hash_new_guid_numeric()
 * would return. The returned value must be free'd with
 * g_hash_table_destroy. */
GHashTable* gnc_sx_all_instantiate_cashflow_all(GDate range_start, GDate range_end);

G_END_DECLS

#endif // _GNC_SX_INSTANCE_MODEL_H
