/********************************************************************\
 * 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>
#ifdef __MINGW32__
#define __USE_MINGW_ANSI_STDIO 1
#endif
#include <errno.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <string>
#include <fstream>

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

static QofLogModule log_module = "gnc.translog";

/*
 * Some design philosophy 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 std::ofstream trans_log_stream; /**< current log file handle */
static std::string trans_log_name; /**< current log file name */
static std::string log_base_name;

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

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

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

void
xaccReopenLog (void)
{
    if (trans_log_stream.is_open())
    {
        xaccCloseLog();
        xaccOpenLog();
    }
}


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

    log_base_name = basepath;

    if (trans_log_stream.is_open())
    {
        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;

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

    base = g_path_get_basename(name);
    bool result = trans_log_name.compare(base) == 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_stream.is_open()) return;

    if (log_base_name.empty())
        log_base_name = "translog";

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

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

    trans_log_stream.open(filename, std::ios::app);
    if (!trans_log_stream.is_open())
    {
        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 */
    auto tmpstr = g_path_get_basename(filename);
    trans_log_name = tmpstr;
    g_free (tmpstr);

    g_free (filename);
    g_free (timestamp);

    /*  Note: this must match src/import-export/log-replay/gnc-log-replay.c */
    trans_log_stream << "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"
                     << "-----------------\n";
}

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

void
xaccCloseLog (void)
{
    if (!trans_log_stream.is_open()) return;
    trans_log_stream.flush();
    trans_log_stream.close();
}

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

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];

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

    gnc_time64_to_iso8601_buff (gnc_time(nullptr), dnow);
    gnc_time64_to_iso8601_buff (trans->date_entered, dent);
    gnc_time64_to_iso8601_buff (trans->date_posted, dpost);
    guid_to_string_buff (xaccTransGetGUID(trans), trans_guid_str);
    trans_notes = xaccTransGetNotes(trans);
    trans_log_stream << "===== START\n";

    for (node = trans->splits; node; node = node->next)
    {
        Split *split = GNC_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';
        }

        gnc_time64_to_iso8601_buff (split->date_reconciled, drecn);

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

        trans_log_stream << flag << '\t'
                         << trans_guid_str << '\t'
                         << split_guid_str << '\t'
                         << dnow << '\t'
                         << dent << '\t'
                         << dpost << '\t'
                         << acc_guid_str << '\t'
                         << (accname ? accname : "") << '\t'
                         << (trans->num ? trans->num : "") << '\t'
                         << (trans->description ? trans->description : "") << '\t'
                         << (trans_notes ? trans_notes : "") << '\t'
                         << (split->memo ? split->memo : "") << '\t'
                         << (split->action ? split->action : "") << '\t'
                         << split->reconciled << '\t'
                         << gnc_numeric_num(amt) << '/' << gnc_numeric_denom(amt) << '\t'
                         << gnc_numeric_num(val) << '/' << gnc_numeric_denom(val) << '\t'
                         << drecn << '\n';
    }

    trans_log_stream << "===== END" << std::endl;
}

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