/* $Id: CCvalidate.cc,v 1.2 1998/09/22 16:56:06 thompson Exp thompson $
 * Copyright (c) 1998 MileStone Solutions, Inc.
 *
 * 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.
 *
 * The GNU General Public License can also be found at:
 *
 *     /http://www.fsf.org/copyleft/gpl.html
 *
 * Requires:
 *
 *     gcc/g++ and libg++
 *
 * To build a test program grep out the following line
 * and pass it to a shell.
g++ -o CCvalidate -DTEST CCvalidate.cc #MakeMe
 *
 * Please forward patches to: thompson@inetnow.net
 */
#include "CCvalidate.h"
//
// Instantiate with a single list of numbers. Generally presumes 
// someone has done some error checking (like it is all numbers)
//
CCvalidate::CCvalidate(char *cc) {
  ccNum = String(cc);
  ccValid();
  ccIssuer();
}
//
// Support for entry as a sequence of (4) 4 digit numbers. Again,
// expects that user interface has validated input.
//
CCvalidate::CCvalidate(char *p1, char *p2, char *p3, char *p4) {
  ccNum = String(p1) + 
    String(p2) +
    String(p3) +
    String(p4);
  ccPartValid(p1,p2,p3,p4);
  ccIssuer();
}

//
// Allow access to determine if the card was any good
//
bool CCvalidate::Valid()
{
  return valid;
}
//
// Sets the issuer using the credit card number and returns
// the issuer as a pointer to a character string.
//
char * CCvalidate::ccIssuer()
{
    String cardNum = ccNum;
    String rv = "Unknown";
    int len = cardNum.length();

    if (len < 2) {
        setIssuer(rv);
        return _Issuer;
    }

    if (cardNum.at(0,2) == "37") {
        rv = "American Express";
    } else if (cardNum.at(0,2) == "56") {
        rv = "BankCard";
    } else if (cardNum[0] == '3') {
        rv = "Diner's Club";
    } else if (cardNum[0] == '5') {
        rv = "MasterCard";
    } else if (cardNum[0] == '4') {
        rv = "Visa";
    } else if (cardNum[0] == '6') {
        rv = "Discover";
    }
    setIssuer(rv);
    return _Issuer;
}
//
// Allow output of the credit card number
//
String CCvalidate::CC()
{
  return ccNum;
}
ostream& operator<<(ostream& s, CCvalidate &c)
{
  return s << c.CC();
}
//----------------------------------------------------------------------
// Private functions
//
// Validate the credit card number and set the class variable 
// appropriately. This presumes that the card number is complete
// (16 numerals)
//
bool CCvalidate::ccValid()
{
    valid = false;
    String cardNum = ccNum;
    int len = cardNum.length();
    if (len < 2)  {
        return false;
    }
    int checksum = 0;

    for (int i = 1; i < len; i++) {
        char ch = cardNum[len - 1 - i];
        int digit = parseInt(ch);
        int temp = digit * (1 + (i % 2));
        if (temp < 10)
          checksum = checksum + temp;
        else
          checksum = checksum + temp - 9;
    }

    checksum = (10 - (checksum % 10)) % 10;

    if (parseInt(cardNum[len - 1]) != checksum)
      return false;

    ccIssuer();
    if (Issuer == "Unknown") {
        return false;
    }
    valid = true;
    return true;
}
//
// Check out the 4-part entry
//
bool CCvalidate::ccPartValid(char *cc1, char *cc2, char *cc3, char *cc4) {
  String ccn1 = String(cc1);
  String ccn2 = String(cc2);
  String ccn3 = String(cc3);
  String ccn4 = String(cc4);
  valid = false;
  int len = ccn1.length();
  if (len != 4)  { return false; }
  len = ccn2.length();
  if (len != 4)  { return false; }
  len = ccn3.length();
  if (len != 4)  { return false; }
  len = ccn4.length();
  if (len != 4)  { return false; }
  ccNum = ccn1 + ccn2 + ccn3 + ccn4;
  valid = ccValid();
  return valid;
}
//
// convert a numeric character to an int
//
int CCvalidate::parseInt(char c)
{
  int rv = 0;
  rv += (c - '0');
  return rv;
}
//
// Use a string to set both the String and character representation of the 
// Issuer
//
void CCvalidate::setIssuer(String i)
{
  Issuer = i;
  memset(_Issuer, 0, sizeof(_Issuer));
  int j = i.length();
  int x;
  for (x = 0; x < j; x++) {
    _Issuer[x] = i[x];
  }
}
void CCvalidate::setIssuer(char * i)
{
  setIssuer(String(i));
}

//----------------------------------------------------------------------
// White-box testing
//
void main(int argc, char **argv) {
  cerr << "test your credit card: " << argv[1] << "\n";
  CCvalidate cc(argv[1]);
  if (cc.Valid()) {
    cerr << "Good number [" << cc << "]\n";
    cerr << "Card from [" << cc.ccIssuer() << "]\n";
  } else {
    cerr << "Bad number [" << argv[1] << "]\n";
  }
  
}
/* EOF: $Id: CCvalidate.cc,v 1.2 1998/09/22 16:56:06 thompson Exp thompson $ */
