/********************************************************************\
 * TransLog.c -- the transaction logger                             *
 * Copyright (C) 1998 Linas Vepstas                                 *
 *                                                                  *
 * 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, 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 <errno.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <string.h>

#include "Account.h"
#include "Transaction.h"
#include "TransactionP.h"
#include "TransLog.h"
#include "qof.h"
#ifdef _MSC_VER
# define g_fopen fopen
#endif

static QofLogModule log_module = "gnc.translog";

/*
 * Some design philosphy that I think would be good to keep in mind:
 * (0) Simplicity and foolproofness are the over-riding design points.
 *     This is supposed to be a fail-safe safety net.   We don't want
 *     our safety net to fail because of some whiz-bang shenanigans.
 *
 * (1) Try to keep the code simple.  Want to make it simple and obvious
 *     that we are recording everything that we need to record.
 *
 * (2) Keep the printed format human readable, for the same reasons.
 * (2.a) Keep the format, simple, flat, more or less unstructured,
 *       record oriented.  This will help parsing by perl scripts.
 *       No, using a perl script to analyze a file that's supposed to
 *       be human readable is not a contradication in terms -- that's
 *       exactly the point.
 * (2.b) Use tabs as a human friendly field separator; its also a
 *       character that does not (should not) appear naturally anywhere
 *       in the data, as it serves no formatting purpose in the current
 *       GUI design.  (hack alert -- this is not currently tested for
 *       or enforced, so this is a very unsafe assumption. Maybe
 *       urlencoding should be used.)
 * (2.c) Don't print redundant information in a single record. This
 *       would just confuse any potential user of this file.
 * (2.d) Saving space, being compact is not a priority, I don't think.
 *
 * (3) There are no compatibility requirements from release to release.
 *     Sounds OK to me to change the format of the output when needed.
 *
 * (-) print transaction start and end delimiters
 * (-) print a unique transaction id as a handy label for anyone
 *     who actually examines these logs.
 *     The C address pointer to the transaction struct should be fine,
 *     as it is simple and unique until the transaction is deleted ...
 *     and we log deletions, so that's OK.  Just note that the id
 *     for a deleted transaction might be recycled.
 * (-) print the current timestamp, so that if it is known that a bug
 *     occurred at a certain time, it can be located.
 * (-) hack alert -- something better than just the account name
 *     is needed for identifying the account.
 */
/* ------------------------------------------------------------------ */


static int gen_logs = 1;
static FILE * trans_log = NULL; /**< current log file handle */
static char * trans_log_name = NULL; /**< current log file name */
static char * log_base_name = NULL;

/********************************************************************\
\********************************************************************/

void xaccLogDisable (void)
{
    gen_logs = 0;
}
void xaccLogEnable  (void)
{
    gen_logs = 1;
}

/********************************************************************\
\********************************************************************/

void
xaccReopenLog (void)
{
    if (trans_log)
    {
        xaccCloseLog();
        xaccOpenLog();
    }
}


void
xaccLogSetBaseName (const char *basepath)
{
    if (!basepath) return;

    g_free (log_base_name);
    log_base_name = g_strdup (basepath);

    if (trans_log)
    {
        xaccCloseLog();
        xaccOpenLog();
    }
}


/*
 * See if the provided file name is that of the current log file.
 * Since the filename is generated with a time-stamp we can ignore the
 * directory path and avoid problems with worrying about any ".."
 * components in the path.
 */
gboolean
xaccFileIsCurrentLog (const gchar *name)
{
    gchar *base;
    gint result;

    if (!name || !trans_log_name)
        return FALSE;

    base = g_path_get_basename(name);
    result = (strcmp(base, trans_log_name) == 0);
    g_free(base);
    return result;
}

/********************************************************************\
\********************************************************************/

void
xaccOpenLog (void)
{
    char * filename;
    char * timestamp;

    if (!gen_logs)
    {
	 PINFO ("Attempt to open disabled transaction log");
	 return;
    }
    if (trans_log) return;

    if (!log_base_name) log_base_name = g_strdup ("translog");

    /* tag each filename with a timestamp */
    timestamp = gnc_date_timestamp ();

    filename = g_strconcat (log_base_name, ".", timestamp, ".log", NULL);

    trans_log = g_fopen (filename, "a");
    if (!trans_log)
    {
        int norr = errno;
        printf ("Error: xaccOpenLog(): cannot open journal \n"
                "\t %d %s\n", norr, g_strerror (norr) ? g_strerror (norr) : "");

        g_free (filename);
        g_free (timestamp);
        return;
    }

    /* Save the log file name */
    if (trans_log_name)
        g_free (trans_log_name);
    trans_log_name = g_path_get_basename(filename);

    g_free (filename);
    g_free (timestamp);

    /*  Note: this must match src/import-export/log-replay/gnc-log-replay.c */
    fprintf (trans_log, "mod\ttrans_guid\tsplit_guid\ttime_now\t"
             "date_entered\tdate_posted\t"
             "acc_guid\tacc_name\tnum\tdescription\t"
             "notes\tmemo\taction\treconciled\t"
             "amount\tvalue\tdate_reconciled\n");
    fprintf (trans_log, "-----------------\n");
}

/********************************************************************\
\********************************************************************/

void
xaccCloseLog (void)
{
    if (!trans_log) return;
    fflush (trans_log);
    fclose (trans_log);
    trans_log = NULL;
}

/********************************************************************\
\********************************************************************/

void
xaccTransWriteLog (Transaction *trans, char flag)
{
    GList *node;
    char trans_guid_str[GUID_ENCODING_LENGTH + 1];
    char split_guid_str[GUID_ENCODING_LENGTH + 1];
    const char *trans_notes;
    char dnow[100], dent[100], dpost[100], drecn[100];
    Timespec ts;

    if (!gen_logs)
    {
	 PINFO ("Attempt to write disabled transaction log");
	 return;
    }
    if (!trans_log) return;

    timespecFromTime64(&ts, gnc_time (NULL));
    gnc_timespec_to_iso8601_buff (ts, dnow);

    timespecFromTime64(&ts, trans->date_entered.tv_sec);
    gnc_timespec_to_iso8601_buff (ts, dent);

    timespecFromTime64(&ts, trans->date_posted.tv_sec);
    gnc_timespec_to_iso8601_buff (ts, dpost);

    guid_to_string_buff (xaccTransGetGUID(trans), trans_guid_str);
    trans_notes = xaccTransGetNotes(trans);
    fprintf (trans_log, "===== START\n");

    for (node = trans->splits; node; node = node->next)
    {
        Split *split = node->data;
        const char * accname = "";
        char acc_guid_str[GUID_ENCODING_LENGTH + 1];
        gnc_numeric amt, val;

        if (xaccSplitGetAccount(split))
        {
            accname = xaccAccountGetName (xaccSplitGetAccount(split));
            guid_to_string_buff(xaccAccountGetGUID(xaccSplitGetAccount(split)),
                                acc_guid_str);
        }
        else
        {
            acc_guid_str[0] = '\0';
        }

        timespecFromTime64(&ts, split->date_reconciled.tv_sec);
        gnc_timespec_to_iso8601_buff (ts, drecn);

        guid_to_string_buff (xaccSplitGetGUID(split), split_guid_str);
        amt = xaccSplitGetAmount (split);
        val = xaccSplitGetValue (split);

        /* use tab-separated fields */
        fprintf (trans_log,
                 "%c\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
                 "%s\t%s\t%s\t%s\t%c\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%s\n",
                 flag,
                 trans_guid_str, split_guid_str,  /* trans+split make up unique id */
                 /* Note that the next three strings always exist,
                 		* so we don't need to test them. */
                 dnow,
                 dent,
                 dpost,
                 acc_guid_str,
                 accname ? accname : "",
                 trans->num ? trans->num : "",
                 trans->description ? trans->description : "",
                 trans_notes ? trans_notes : "",
                 split->memo ? split->memo : "",
                 split->action ? split->action : "",
                 split->reconciled,
                 gnc_numeric_num(amt),
                 gnc_numeric_denom(amt),
                 gnc_numeric_num(val),
                 gnc_numeric_denom(val),
                 /* The next string always exists. No need to test it. */
                 drecn);
    }

    fprintf (trans_log, "===== END\n");

    /* get data out to the disk */
    fflush (trans_log);
}

/************************ END OF ************************************\
\************************* FILE *************************************/
