/*
 * Copyright 2013 - Scriptel Corporation
 */
package com.scriptel.easyscript;

/**
 * This class represents the parsed magnetic stripe data from a financial
 * (credit) card.
 * @see <a href="http://en.wikipedia.org/wiki/Magnetic_stripe_card">http://en.wikipedia.org/wiki/Magnetic_stripe_card</a>
 */
public class FinancialCard {
    /**
     * Parsed first track from the magnetic strip.
     */
    private FinancialCardTrackOne trackOne;
    /**
     * Parsed second track from the magnetic strip.
     */
    private FinancialCardTrackTwo trackTwo;
    /**
     * Indicates whether or not the parsed card number passes the Luhn checksum algorithm. Used to detect minor errors in card entry.
     */
    private boolean numberValid;
    /**
     * Stores the financial card issuer if it can be identified.
     */
    private FinancialCardIssuer issuer;

    /**
     * This method takes the raw card data from the swipe and attempts to
     * extract financial card data.
     * @param cardData Raw magnetic strip data.
     * @return FinancialCard object containing parsed data if possible, otherwise null.
     */
    public static FinancialCard parse(String cardData) {
        FinancialCardTrackOne t1 = FinancialCardTrackOne.parse(cardData);
        FinancialCardTrackTwo t2 = FinancialCardTrackTwo.parse(cardData);
        
        if(t1!=null || t2!=null) {
            FinancialCard card = new FinancialCard();
            card.setTrackOne(t1);
            card.setTrackTwo(t2);
            String cardNumber = (t1!=null)?t1.getAccountNumber():t2.getAccountNumber();
            card.setIssuer(FinancialCardIssuer.identifyByCardNumber(cardNumber));
            card.setNumberValid(FinancialCard.verifyCardChecksum(cardNumber));
            return card;
        }
        return null;
    }
    /**
     * Returns this object as a human readable string.
     * @return String containing human readable string.
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        String ln = System.getProperty("line.separator");
        sb.append("Financial Card").append(ln);
        if(trackOne!=null) {
            sb.append(trackOne).append(ln);
        }
        if(trackTwo!=null) {
            sb.append(trackTwo).append(ln);
        }
        sb.append("Number Valid: ").append(numberValid).append(ln);
        sb.append("      Issuer: ").append(issuer).append(ln);
        
        return sb.toString();
    }
    /**
     * This method takes a card number and applies Luhn's algorithm to verify
     * the card checksum. This method can detect minor transpositions and
     * obviously invalid card numbers, but it cannot detect fake credit card
     * numbers.
     * @see <a href="http://en.wikipedia.org/wiki/Luhn_algorithm">http://en.wikipedia.org/wiki/Luhn_algorithm</a>
     * @param cardNumber Card number to verify.
     * @return True if the card number passes, false otherwise.
     */
    public static boolean verifyCardChecksum(String cardNumber) {
        int sum = 0, flip = 0;
        int[][] table = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },{ 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 } };
        for(int i=cardNumber.length()-1;i>=0;i--) {
            sum += table[flip++ & 1][Integer.parseInt(Character.toString(cardNumber.charAt(i)))];
        }
        return sum % 10 == 0;
    }
    
    /**
     * Gets track one.
     * @return Parsed track one.
     */
    public FinancialCardTrackOne getTrackOne() {
        return trackOne;
    }

    /**
     * Sets track one.
     * @param trackOne the trackOne to set
     */
    public void setTrackOne(FinancialCardTrackOne trackOne) {
        this.trackOne = trackOne;
    }

    /**
     * Gets track two.
     * @return Parsed track two.
     */
    public FinancialCardTrackTwo getTrackTwo() {
        return trackTwo;
    }

    /**
     * Sets track two.
     * @param trackTwo the trackTwo to set
     */
    public void setTrackTwo(FinancialCardTrackTwo trackTwo) {
        this.trackTwo = trackTwo;
    }

    /**
     * Gets whether or not the credit card number passes Luhn's algorithm.
     * @return the numberValid
     */
    public boolean isNumberValid() {
        return numberValid;
    }

    /**
     * Sets whether or not the credit card number passes Luhn's algorithm.
     * @param numberValid the numberValid to set
     */
    public void setNumberValid(boolean numberValid) {
        this.numberValid = numberValid;
    }

    /**
     * Gets the card issuer determined based on card number patterns.
     * @return The card issuer.
     */
    public FinancialCardIssuer getIssuer() {
        return issuer;
    }

    /**
     * Sets the card issuer.
     * @param issuer Card issuer.
     */
    public void setIssuer(FinancialCardIssuer issuer) {
        this.issuer = issuer;
    }
}
