// <copyright file="FinancialCard.cs" company="Scriptel Corporation">
//      Copyright 2014 - Scriptel Corporation
// </copyright>
namespace EasyScriptAPI
{
    using System;
    using System.Text;

    /// <summary>
    /// This class represents the parsed magnetic stripe data from a financial
    /// (credit) card. See http://en.wikipedia.org/wiki/Magnetic_stripe_card">http://en.wikipedia.org/wiki/Magnetic_stripe_card
    /// </summary>
    public class FinancialCard
    {
        /// <summary>
        /// Parsed first track from the magnetic strip.
        /// </summary>
        private FinancialCardTrackOne trackOne;

        /// <summary>
        /// Parsed second track from the magnetic strip.
        /// </summary>
        private FinancialCardTrackTwo trackTwo;

        /// <summary>
        /// Indicates whether or not the parsed card number passes the Luhn checksum algorithm. Used to detect minor errors in card entry.
        /// </summary>
        private bool numberValid;

        /// <summary>
        /// Stores the financial card issuer if it can be identified.
        /// </summary>
        private FinancialCardIssuer issuer;

        /// <summary>
        /// This method takes the raw card data from the swipe and attempts to
        /// extract financial card data.
        /// </summary>
        /// <param name="cardData">raw magnetic strip data</param>
        /// <returns>FinancialCard object containing parsed data if possible, otherwise null.</returns>
        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;
        }

        /// <summary>
        /// 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 http://en.wikipedia.org/wiki/Luhn_algorithm
        /// </summary>
        /// <param name="cardNumber">the credit card number to verify</param>
        /// <returns>True if the card number passes, false otherwise</returns>
        public static bool 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, int.Parse(cardNumber[i] + string.Empty)];
            }

            return sum % 10 == 0;
        }

        /// <summary>
        /// Returns a string representation of this object
        /// </summary>
        /// <returns>string representing this object.</returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            string ln = Environment.NewLine;
            sb.Append("Financial Card").Append(ln);
            if (this.trackOne != null)
            {
                sb.Append(this.trackOne).Append(ln);
            }

            if (this.trackTwo != null)
            {
                sb.Append(this.trackTwo).Append(ln);
            }

            sb.Append("Number Valid: ").Append(this.numberValid).Append(ln);
            sb.Append("      Issuer: ").Append(this.issuer.GetIssuerName()).Append(ln);
        
            return sb.ToString();
        }
    
        /// <summary>
        /// Returns the track one data
        /// </summary>
        /// <returns>the track one data</returns>
        public FinancialCardTrackOne GetTrackOne()
        {
            return this.trackOne;
        }
        
        /// <summary>
        /// Gets the track two data
        /// </summary>
        /// <returns>the track two data</returns>
        public FinancialCardTrackTwo GetTrackTwo()
        {
            return this.trackTwo;
        }

        /// <summary>
        /// Gets whether or not the card passes Luhn's algorithm
        /// </summary>
        /// <returns>whether the card is valid based on Luhn's algorithm.</returns>
        public bool IsNumberValid()
        {
            return this.numberValid;
        }

        /// <summary>
        /// Gets the card issuer determined based on card patterns
        /// </summary>
        /// <returns>the card issuer</returns>
        public FinancialCardIssuer GetIssuer()
        {
            return this.issuer;
        }

        /// <summary>
        /// Sets the track one data
        /// </summary>
        /// <param name="trackOne">the track one data</param>
        private void SetTrackOne(FinancialCardTrackOne trackOne)
        {
            this.trackOne = trackOne;
        }

        /// <summary>
        /// Sets the card issuer
        /// </summary>
        /// <param name="issuer">The card issuer</param>
        private void SetIssuer(FinancialCardIssuer issuer)
        {
            this.issuer = issuer;
        }

        /// <summary>
        /// Sets whether or not the card passes Luhn's algorithm
        /// </summary>
        /// <param name="numberValid">whether or not the card passes Luhn's algorithm</param>
        private void SetNumberValid(bool numberValid)
        {
            this.numberValid = numberValid;
        }

        /// <summary>
        /// Sets the track two data
        /// </summary>
        /// <param name="trackTwo">the track two data</param>
        private void SetTrackTwo(FinancialCardTrackTwo trackTwo)
        {
            this.trackTwo = trackTwo;
        }
    }
}
