/******************************************************************************/
/*                                                                            */
/*     Copyright (C)    2006-2006 Benjamin Drieu (bdrieu@april.org)           */
/*          https://www.grisbi.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; 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 */
/*                                                                            */
/******************************************************************************/

#include "config.h"

#include "include.h"

#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#else
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#endif

/* this libofx/libofx.h header file generates
 /usr/local/Cellar/libofx/0.10.0/include/libofx/libofx.h:113:42: error: this function declaration is not a prototype [-Werror,-Wstrict-prototypes]
  LibofxContextPtr libofx_get_new_context();
                                         ^
                                          void
*/
#include <libofx/libofx.h>

#if defined(__GNUC__)
#pragma GCC diagnostic pop
#else
#pragma clang diagnostic pop
#endif

#include <glib/gi18n.h>

/*START_INCLUDE*/
#include "ofx.h"

#include "dialog.h"
#include "grisbi_win.h"
#include "gsb_real.h"
#include "structures.h"
#include "utils_str.h"
#include "erreur.h"
/*END_INCLUDE*/

/*START_EXTERN*/
/*END_EXTERN*/

/*START_STATIC*/
/* on doit mettre le compte en cours d'importation en global pour que */
/* la libofx puisse le traiter de plus un fichier ofx peut intégrer */
/* plusieurs comptes, donc on crée une liste... */
static GSList *					liste_comptes_importes_ofx;
static struct ImportAccount *	compte_ofx_importation_en_cours;

static gchar *					ofx_filename;
static gint						erreur_import_ofx;
static gint						message_erreur_operation;
static const gchar *			coding_system = NULL;

static LibofxContextPtr			ofx_context;
/*END_STATIC*/

/******************************************************************************/
/* Private Methods                                                            */
/******************************************************************************/
/**
 *
 *
 * \param
 * \param
 *
 * \return
 **/
static int ofx_proc_account_cb (struct OfxAccountData data,
								void *account_data)
{
	/* printf ("ofx_proc_account_cb\n"); */
	/* printf ("account_id_valid %d\n", data.account_id_valid); */
	/* printf ("account_id %s\n", data.account_id); */
	/* printf ("account_name %s\n", data.account_name); */
	/* printf ("account_type_valid %d\n", data.account_type_valid); */
	/* printf ("account_type %d\n", data.account_type); */
	/* printf ("currency_valid %d\n", data.currency_valid); */
	/* printf ("currency %s\n", data. currency); */

	/* si on revient ici et qu'un compte était en cours, c'est qu'il est fini et qu'on passe au compte */
	/* suivant... */

	if (compte_ofx_importation_en_cours)
		liste_comptes_importes_ofx = g_slist_append (liste_comptes_importes_ofx,
													 compte_ofx_importation_en_cours);

	compte_ofx_importation_en_cours = g_malloc0 (sizeof (struct ImportAccount));

	if (data.account_id_valid)
	{
		gchar *tmp_str;
		compte_ofx_importation_en_cours->id_compte = g_convert (data.account_id, -1, "UTF-8", coding_system, NULL, NULL, NULL);
		tmp_str = g_convert (data.account_name, -1, "UTF-8", coding_system, NULL, NULL, NULL);
		compte_ofx_importation_en_cours->nom_de_compte = gsb_import_unique_imported_name (tmp_str);
		g_free(tmp_str);
		compte_ofx_importation_en_cours->filename = ofx_filename;
	}

	compte_ofx_importation_en_cours->real_filename = g_strdup (ofx_filename);
	compte_ofx_importation_en_cours->origine = g_strdup ("OFX");

	if (data.account_type_valid)
		compte_ofx_importation_en_cours->type_de_compte = data.account_type;

	if (data.currency_valid)
		compte_ofx_importation_en_cours->devise = g_convert (data.currency, -1, "UTF-8", coding_system, NULL, NULL, NULL);

	return 0;
}

 /**
 *
 *
 * \param
 * \param
 *
 * \return
 **/

 static int ofx_proc_statement_cb (struct OfxStatementData data,
								   void *statement_data)
{
	GDate *date;

	/* printf ("ofx_proc_statement_cb\n"); */
	/* printf ("currency_valid : %d\n", data.currency_valid); */
	/* printf ("currency : %s\n", data.currency); */
	/* printf ("account_id_valid : %d\n", data.account_id_valid); */
	/* printf ("account_id : %s\n", data.account_id); */
	/* printf ("ledger_balance_valid : %d\n", data.ledger_balance_valid); */
	/* printf ("ledger_balance : %f\n", data.ledger_balance); */
	/* printf ("ledger_balance_date_valid : %d\n", data.ledger_balance_date_valid); */
	/* printf ("ledger_balance_date : %s\n", ctime (&data.ledger_balance_date)); */
	/* printf ("available_balance_valid : %d\n", data.available_balance_valid); */
	/* printf ("available_balance : %f\n", data.available_balance); */
	/* printf ("available_balance_date_valid : %d\n", data.available_balance_date_valid); */
	/* printf ("available_balance_date : %s\n", ctime (&data.available_balance_date)); */
	/* printf ("date_start_valid : %d\n", data.date_start_valid); */
	/* printf ("date_start : %s\n", ctime (&data.date_start)); */
	/* printf ("date_end_valid : %d\n", data.date_end_valid); */
	/* printf ("date_end : %s\n", ctime (&data.date_end)); */
	/* printf ("marketing_info_valid : %d\n", data.marketing_info_valid); */
	/* printf ("marketing_info : %s\n", data.marketing_info); */

	if (data.date_start_valid)
	{
		date = g_date_new ();

		g_date_set_time_t (date, data.date_start);
		if (g_date_valid (date))
			compte_ofx_importation_en_cours->date_depart = date;
	}

	if (data.date_end_valid)
	{
		date = g_date_new ();

		g_date_set_time_t (date, data.date_end);
		if (g_date_valid (date))
			compte_ofx_importation_en_cours->date_fin = date;
	}

	return 0;
}

 /**
 *
 *
 * \param
 * \param
 *
 * \return
 **/
static int ofx_proc_status_cb (struct OfxStatusData data,
							  void *status_data)
{
	/* printf ("ofx_proc_status_cb:\n"); */
	/* printf ("ofx_element_name_valid %d\n", data . ofx_element_name_valid); */
	/* printf ("ofx_element_name %s\n", data . ofx_element_name); */
	/* printf ("code_valid %d\n", data . code_valid); */
	/* printf ("code %d\n", data . code); */
	/* printf ("name %s\n", data . name); */
	/* printf ("description %s\n", data . description); */
	/* printf ("severity_valid %d\n", data . severity_valid); */
	/* printf ("severity %d\n", data . severity); */
	/* printf ("server_message_valid %d\n", data.server_message_valid); */
	/* printf ("server_message %s\n\n", data . server_message); */

	/* on vérifie l'état, si c'est un warning, on l'affiche, si c'est une erreur, on vire */
	if (data.severity_valid)
	{
		switch (data.severity)
		{
			case INFO :
			/* 		pas de pb, on fait rien */
			break;

			case WARN :
			if (data.code_valid)
				dialogue_warning (g_strconcat (_("OFX processing returned following message:\n"),
											   data.name,
											   "\n",
											   data.description,
											   NULL));
			else
				dialogue_warning (_("OFX processing ended in a warning message which is not valid."));
			/* erreur_import_ofx = 1; */
			break;

			case ERROR:
			if (data.code_valid)
				dialogue_error (g_strconcat (_("OFX processing returned following error message:\n"),
											 data.name,
											 "\n",
											 data.description,
											 NULL));
			else
				dialogue_error (_("OFX processing returned an error message which is not valid."));
			erreur_import_ofx = 1;
			break;
		}
	}

	return 0;
}

 /**
 *
 *
 * \param
 * \param
 *
 * \return
 **/
static int ofx_proc_transaction_cb (struct OfxTransactionData data,
									void *security_data)
{
	struct ImportTransaction *ope_import;
	GDate *date;

	/* printf ("ofx_proc_transaction_cb\n"); */
	/* printf ("account_id_valid : %d  \n", data.account_id_valid); */
	/* printf ("account_id : %s  \n", data.account_id); */
	/* printf ("transactiontype_valid : %d  \n", data.transactiontype_valid); */
	/* printf ("transactiontype : %d  \n", data.transactiontype); */
	/* printf ("invtransactiontype_valid : %d  \n", data.invtransactiontype_valid); */
	/* printf ("invtransactiontype : %d  \n", data.invtransactiontype); */
	/* printf ("units_valid : %d  \n", data. units_valid); */
	/* printf ("units : %f  \n", data.units); */
	/* printf ("unitprice_valid : %d  \n", data.unitprice_valid); */
	/* printf ("unitprice : %f  \n", data.unitprice); */
	/* printf ("amount_valid : %d  \n", data.amount_valid); */
	/* printf ("amount : %f  \n", data.amount); */
	/* printf ("fi_id_valid : %d  \n", data.fi_id_valid); */
	/* printf ("fi_id : %s  \n", data.fi_id); */
	/* printf ("unique_id_valid : %d  \n", data.unique_id_valid); */
	/* printf ("unique_id : %s  \n", data.unique_id); */
	/* printf ("unique_id_type_valid : %d  \n", data.unique_id_type_valid); */
	/* printf ("unique_id_type : %s  \n", data.unique_id_type); */
	/* printf ("security_data_valid : %d  \n", data.security_data_valid); */
	/* printf ("security_data_ptr : %s  \n", data.security_data_ptr); */
	/* printf ("date_posted_valid : %d  \n", data.date_posted_valid); */
	/* printf ("date_posted : %s  \n", ctime (&data.date_posted)); */
	/* printf ("date_initiated_valid : %d  \n", data.date_initiated_valid); */
	/* printf ("date_initiated : %s  \n", ctime (&data.date_initiated)); */
	/* printf ("date_funds_available_valid : %d  \n", data.date_funds_available_valid); */
	/* printf ("date_funds_available : %s  \n", ctime (&data.date_funds_available)); */
	/* printf ("fi_id_corrected_valid : %d  \n", data.fi_id_corrected_valid); */
	/* printf ("fi_id_corrected : %s  \n", data.fi_id_corrected); */
	/* printf ("fi_id_correction_action_valid : %d  \n", data.fi_id_correction_action_valid); */
	/* printf ("fi_id_correction_action : %d  \n", data.fi_id_correction_action); */
	/* printf ("server_transaction_id_valid : %d  \n", data.server_transaction_id_valid); */
	/* printf ("server_transaction_id : %s  \n", data.server_transaction_id); */
	/* printf ("check_number_valid : %d  \n", data.check_number_valid); */
	/* printf ("check_number : %s  \n", data.check_number); */
	/* printf ("reference_number_valid : %d  \n", data.reference_number_valid); */
	/* printf ("reference_number : %s  \n", data.reference_number); */
	/* printf ("standard_industrial_code_valid : %d  \n", data.standard_industrial_code_valid); */
	/* printf ("standard_industrial_code : %s  \n", data.standard_industrial_code); */
	/* printf ("payee_id_valid : %d  \n", data.payee_id_valid); */
	/* printf ("payee_id : %s  \n", data.payee_id); */
	/* printf ("name_valid : %d  \n", data.name_valid); */
	/* printf ("name : %s  \n", data.name); */
	/* printf ("memo_valid : %d  \n", data.memo_valid); */
	/* printf ("memo : %s  \n\n\n\n", data.memo); */

	/* si à ce niveau le comtpe n'est pas créé, c'est qu'il y a un pb... */
	if (!compte_ofx_importation_en_cours)
 	{
		if (!message_erreur_operation)
		{
			dialogue_error (_("A transaction try to be saved but no account was created...\n"));
			message_erreur_operation = 1;
			erreur_import_ofx = 1;
		}
		return 0;
	}

	/* c'est parti, on crée et remplit l'opération */
	ope_import = g_malloc0 (sizeof (struct ImportTransaction));

	if (data.fi_id_valid)
		ope_import->id_operation = g_convert (data.fi_id, -1, "UTF-8", coding_system, NULL, NULL, NULL);

	date = g_date_new ();
	if (data.date_posted_valid)
	{
		g_date_set_time_t (date, data.date_posted);
		if (g_date_valid (date))
			ope_import->date_de_valeur = date;
		else
			ope_import->date_de_valeur = NULL;
	}

	if (data.date_initiated_valid)
	{
		g_date_set_time_t (date, data.date_initiated);
		if (g_date_valid (date))
			ope_import->date = date;
		else
			ope_import->date = ope_import->date_de_valeur;
	}
	else
		ope_import->date = ope_import->date_de_valeur;

	if (data.name_valid)
		ope_import->tiers = g_convert (data.name, -1, "UTF-8", coding_system, NULL, NULL, NULL);

	if (data.memo_valid)
		ope_import->notes = g_convert (data.memo, -1, "UTF-8", coding_system, NULL, NULL, NULL);

	if (data.check_number_valid)
		ope_import->cheque = g_convert (data.check_number, -1, "UTF-8", coding_system, NULL, NULL, NULL);

	if (data.amount_valid)
		ope_import->montant = gsb_real_double_to_real (data.amount);

	if (data.transactiontype_valid)
		ope_import->type_de_transaction = data.transactiontype;

	/* on peut faire ici des petites modifs en fonction du type de transaction, */
	/* mais tout n'est pas utilisé par les banques... */
	if (data.transactiontype_valid)
	{
		GrisbiWinEtat *w_etat;

		w_etat = (GrisbiWinEtat *) grisbi_win_get_w_etat ();

		switch (data.transactiontype)
		{
			case OFX_CHECK:
				/* si c'est un chèque, svt ya pas de tiers, on va mettre chèque...  */
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Check"));
				break;
			case OFX_INT:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Interest"));
				break;
			case OFX_DIV:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Dividend"));
				break;
			case OFX_SRVCHG:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Service charge"));
				break;
			case OFX_FEE:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Fee"));
				break;
			case OFX_DEP:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Deposit"));
				break;
			case OFX_ATM:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Cash dispenser"));
				break;
			case OFX_POS:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Point of sale"));
				break;
			case OFX_XFER:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Transfer"));
				break;
			case OFX_PAYMENT:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Electronic payment"));
				break;
			case OFX_CASH:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Cash"));
				break;
			case OFX_DIRECTDEP:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Direct deposit"));
				break;
			case OFX_DIRECTDEBIT:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Merchant initiated debit"));
				break;
			case OFX_REPEATPMT:
				if (!ope_import->tiers)
					ope_import->tiers = g_strdup (_("Repeating payment/standing order"));
				break;
			case OFX_DEBIT:
			case OFX_CREDIT:
			case OFX_OTHER:
				if (w_etat->extract_number_for_check)
				{
					gchar *str_to_free = NULL;

					str_to_free = utils_str_my_case_strstr (ope_import->tiers, _("Check"));
					if (str_to_free)
					{
						gchar *tmp_str;
						tmp_str = gsb_string_extract_int (ope_import->tiers);
						if (tmp_str && strlen (tmp_str) > 0)
						{
							g_free(ope_import->cheque);
							ope_import->cheque = tmp_str;
							ope_import->type_de_transaction = GSB_CHECK;
						}
						else if (tmp_str && strlen (tmp_str) == 0)
						{
							g_free (tmp_str);
						}
						g_free (str_to_free);
					}
				}
				break;
		}
	}
	/* on ajoute l'opé à son compte */
	compte_ofx_importation_en_cours->operations_importees = g_slist_append (compte_ofx_importation_en_cours->operations_importees,
																			ope_import);

	return 0;
}

/******************************************************************************/
/* Public Methods                                                             */
/******************************************************************************/
/**
 *
 *
 * \param
 * \param
 *
 * \return
 **/
gboolean recuperation_donnees_ofx (GtkWidget *assistant,
								   struct ImportFile *imported)
{
	GSList *liste_tmp;

	liste_comptes_importes_ofx = NULL;
	compte_ofx_importation_en_cours = NULL;
	erreur_import_ofx = 0;
	message_erreur_operation = 0;
	ofx_filename = imported->name;
	coding_system = imported->coding_system;

	devel_debug (imported->name);

	ofx_context = libofx_get_new_context();
	ofx_set_status_cb (ofx_context, ofx_proc_status_cb, NULL);
	/* ofx_set_security_cb (ofx_context, sofx_proc_security_cb, NULL); */
	ofx_set_account_cb (ofx_context, ofx_proc_account_cb, NULL);
	ofx_set_transaction_cb (ofx_context, ofx_proc_transaction_cb, NULL);
	ofx_set_statement_cb (ofx_context, ofx_proc_statement_cb, NULL);

	libofx_proc_file (ofx_context, ofx_filename, AUTODETECT);

	/* le dernier compte n'a pas été ajouté à la liste */
	liste_comptes_importes_ofx = g_slist_append (liste_comptes_importes_ofx, compte_ofx_importation_en_cours);

	if (!compte_ofx_importation_en_cours)
	{
		struct ImportAccount * account;

		account = g_malloc0 (sizeof (struct ImportAccount));
		account->nom_de_compte = gsb_import_unique_imported_name (_("Invalid OFX file"));
		account->filename = g_strdup (ofx_filename);
		account->real_filename = g_strdup (ofx_filename);
		account->origine = g_strdup ("OFX");
		gsb_import_register_account_error (account);
		devel_debug (account->nom_de_compte);

		return FALSE ;
	}

	liste_tmp = liste_comptes_importes_ofx;
	while (liste_tmp)
	{
		if (!erreur_import_ofx)
		{
			gsb_import_register_account (liste_tmp->data);
		}
		else
		{
			gsb_import_register_account_error (liste_tmp->data);
 		}

		liste_tmp = liste_tmp->next;
	}

	return TRUE;
}

/**
 *
 *
 * \param
 *
 * \return
 **/
/* Local Variables: */
/* c-basic-offset: 4 */
/* End: */
