/***************************************************************************
                             -------------------
    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: This help message\n"
	   << "  --version: 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;
}
