/***************************************************************************
                             -------------------
    cvs         : $Id$
    begin       : Sat Aug 10 2002
    copyright   : (C) 2002 by Fabian Kaiser
    email       : fabian@openhbci.de
   modified     : m.plugge@fh-mannheim.de for file check

 ***************************************************************************
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Lesser General Public            *
 *   License as published by the Free Software Foundation; either          *
 *   version 2.1 of the License, or (at your option) any later version.    *
 *                                                                         *
 *   This library 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     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
 *   MA  02111-1307  USA                                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#else
# define VERSION ""
# define PACKAGE "ktoblzcheck"
#endif

#include <ktoblzcheck.h>

#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <fstream>

using namespace std;

typedef enum _Outformat {
    MULTILINE, ONELINE
} Outformat;
string bankId = "";
string accountId = "";
string bankdataFile = "";
string method = "";
string infile = "";
string outfile = "";
bool justReturnCode = false;
int nextArg = 1;
Outformat outformat = MULTILINE;

void printUsage()
{
    cout << "Usage:" << endl;
    cout << PACKAGE
         << " [--returncode] [--file=datafile] <bank-id> <account-id>\n"
         << "  --help, -h:             This help message\n"
         << "  --version, -v:          Print the version number and exit.\n"
         << "  --bankdata-path:        Print the path where bankdata files are expected, and exit.\n"
         << "  --bankdata-looked-up:   Print the looked-up bankdata file and path that \n"
         << "                           will be used, and exit.\n"
         << "  --file=<bankdata-file>: Use this specified file as bankdata\n"
         << "  --returncode:           Switch off output. Result is returned only via exit code\n"
         << "  --outformat=<format>:   Choose output format: \"multiline\" (default), \n"
         << "                           or \"oneline\" (tab-delimited) output\n"
         << "  --infile=<filename>:    Read many BLZ/accounts from filename, not cmdline\n"
         << "  --outfile=<filename>:   Write results to filename instead of stdout"
         << endl;

    cout
        << "                0: Everything is ok: account and bank match\n"
        << "                1: Validation algorithm is unknown/unimplemented\n"
        << "                2: Account and bank do not match\n"
        << "                3: No bank with given bank-id found\n"
        << endl;

    exit(1);
}

void checkArg(string argument)
{
    string argn1 = "--method=";
    string argn2 = "--outformat=";
    string argn3 = "--file=";
    string argn4 = "--infile=";
    string argn5 = "--outfile=";

    if (argument == "--version" || argument == "-V") {
        cout << PACKAGE " " VERSION << endl;
        exit(0);
    } else if (argument == "--help" || argument == "-h") {
        printUsage();
    } else if (argument == "--bankdata-path") {
        std::cout << "BANKDATA_PATH=" << AccountNumberCheck::bankdata_dir() << std::endl;
        exit(0);
    } else if (argument == "--bankdata-looked-up") {
        AccountNumberCheck anc;
        std::string result = anc.getFilenameClosestDateToday();
        std::cout << "BANKDATA_LOOKED_UP=" << result << std::endl;
        exit(0);
    } else if (argument == "--returncode") {
        nextArg++;
        justReturnCode = true;
    } else if (argument.length() > string(argn1).length()
               && argument.substr(0, string(argn1).length()) == argn1) {
        method = argument.substr(string(argn1).length());
        nextArg++;
    } else if (argument.length() > string(argn2).length()
               && argument.substr(0, string(argn2).length()) == argn2) {
        string argvalue = argument.substr(string(argn2).length());
        if (argvalue == "multiline") {
            outformat = MULTILINE;
        } else if (argvalue == "oneline") {
            outformat = ONELINE;
        } else {
            std::cerr
                << PACKAGE
            ": The parameter --outformat has an unknown value. Allowed values are \"multiline\" and \"oneline\"."
                << endl;
        }

        nextArg++;
    } else if (argument.length() > string(argn3).length()
               && argument.substr(0, string(argn3).length()) == argn3) {
        bankdataFile = argument.substr(string(argn3).length());
        nextArg++;
    } else if (argument.length() > string(argn4).length()
               && argument.substr(0, string(argn4).length()) == argn4) {
        infile = argument.substr(string(argn4).length());
        nextArg++;
    } else if (argument.length() > string(argn5).length()
               && argument.substr(0, string(argn5).length()) == argn5) {
        outfile = argument.substr(string(argn5).length());
        nextArg++;
    } else {
        if (bankId.empty()) {
            bankId = argument;
        } else if (accountId.empty()) {
            accountId = argument;
        } else {
            std::cerr << PACKAGE ": Warning: extra command line arguments ignored" << endl;
        }
    }
}

int main(int argc, char **argv)
{
    for (int i = 1; i < argc; i++) {
        checkArg(argv[i]);
    }

    if (argc - nextArg < 2) {
        printUsage();
    }

    // Can we open the specified bankdata file? If not, use the
    // installed data file
    AccountNumberCheck *check_ptr = 0;
    if (bankdataFile.empty()) {
        check_ptr = new AccountNumberCheck();
//       time_t mydate = time(NULL);//-24*3600*30*9;
//       if (!check_ptr->loadDataForDate(mydate))
//       {
//   std::time_t x = check_ptr->closestValidData(mydate);
//   std::cerr << PACKAGE ": Warning: The bankdata files which are known to your installed version of libktoblzcheck are not valid today.  If you think up-to-date files should be available, you will have to update the installed libktoblzcheck library as well.  The closest known available date with valid bankdata is: " << ctime(&x)
//         << std::endl;
//       }
        // No need to load the data file a second time - the default
        // constructor already loads the most suitable file for today.
    } else {
        ifstream test(bankdataFile.c_str());
        if (test.fail()) {
            std::cerr << PACKAGE ": Warning: Specified bankdata file '"
                      << bankdataFile
                      << "' could not be opened. Trying default file."
                      << std::endl;
            check_ptr = new AccountNumberCheck();
        } else {
            check_ptr = new AccountNumberCheck(bankdataFile);
        }
    }
    assert(check_ptr);

    AccountNumberCheck &checker = *check_ptr;
    AccountNumberCheck::Result finalresult = AccountNumberCheck::OK;

    if (!infile.empty()) {
        // Read BLZ/Account from input file
        FILE *in, *out;
        if (infile == "-") {
            in = stdin;
        } else if (!(in = fopen(infile.c_str(), "r"))) {
            fprintf(stderr, "cannot open input file \"%s\" for reading; aborting.\n",
                    infile.c_str());
            exit(1);
        }
        if (outfile.empty()) {
            out = stdout;
        } else {
            if (!(out = fopen(outfile.c_str(), "w"))) {
                fprintf(stderr, "cannot open output file \"%s\" for writing; aborting.\n",
                        outfile.c_str());
                exit(1);
            }
        }
        AccountNumberCheck::Result result;
        string text;
        char *blz;
        char *kto;
        char *ptr;
#define LINEBUFSIZE 1024
        char linebuffer[LINEBUFSIZE];
        while (!feof(in)) {
            if (!fgets(linebuffer, LINEBUFSIZE, in)) {
                break;
            }
            for (ptr = linebuffer; isspace(*ptr); ptr++) {
            }
            blz = ptr;
            while (isdigit(*ptr)) {
                ptr++;
            }
            *ptr++ = 0;
            kto = ptr;
            while (isdigit(*ptr)) {
                ptr++;
            }
            *ptr = 0;
            result = checker.check(blz, kto, method);
            finalresult = (result == AccountNumberCheck::OK ? finalresult : result);
            if (!justReturnCode) {
                text = AccountNumberCheck::resultToString(result);
                // FIXME: The output should be unified with the lower
                // "oneline" output format!
                fprintf(out, "%d blz: %s, kto: %s ==> %s\n", result, blz, kto, text.c_str());
            }
        }
    } else {
        // Read BLZ/Account from cmdline
        bool found = true;
        AccountNumberCheck::Record bankData;
        try {
            bankData = checker.findBank(bankId);
        } catch (int i) {
            found = false;
        }

        finalresult = checker.check(bankId, accountId, method);

        if (!justReturnCode) {
            switch (outformat) {
            case MULTILINE:
                cout << "Bank: "
                     << (found
                    ? ("'" + bankData.bankName + "' at '" + bankData.location + "'")
                    : "<unknown>") << " (" << bankId << ")" << endl
                     << "Account: " << accountId << endl
                     << "Result is: " << "(" << finalresult << ") "
                     << AccountNumberCheck::resultToString(finalresult) << endl
                     << endl;
                break;
            case ONELINE:
                // FIXME: The output should be unified with the
                // file-based output above!
                string unknown("<unknown>");
                cout << (found ? bankData.bankName : unknown) << "\t"
                     << (found ? bankData.location : unknown) << "\t"
                     << AccountNumberCheck::resultToString(finalresult) << "\t"
                     << bankId << "\t"
                     << accountId << "\t"
                     << (found ? bankData.method : unknown)
                     << endl;
            }
        }
    }

    delete check_ptr;
    return finalresult;
}
