/*
  /// This class writes DTAUS files for german banks, direct debit orders
  /// see http://www.infodrom.org/projects/dtaus/dtaus.html for description of file format;
  /// the data comes from special request objects
 * DTA Specifiation: see http://www.ebics-zka.de/dokument/pdf/Anlage%203-Spezifikation%20der%20Datenformate%20-%20Version%202.3%20Endfassung%20vom%2005.11.2008.pdf
 * Author: Timotheus Pokorra (http://tpokorra.blogspot.com/2008/09/dtaus-with-c.html)
 * Date: 22/09/2008
 * Feel free to use this code in any way you like
 * Florian Harbich: The extended code you have here is licensed as "Creative Commons NC-BY-SA"
 * Please contact me at florian.harbich (at) die-rooter.de if you want to use this code
 * in commercial projects.
 * This file is licensed under Creative Commons Namensnennung-Keine kommerzielle Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland license.
 * 
 * Changelog:
 * May 2009: Timotheus Pokorra: fixed a bug with long references, make more extensions possible
 * Nov 2009: Florian Harbich (http://www.die-rooter.de/ITworks/archives/26-Writing-DTAUS-files-with-C-DTA-Dateien-mit-C-erzeugen.html):
 *           - changed input data type to special request object classes
 *           - fixed writing of dataset C extensions for all 13 allowed extra reference lines
 *           - fixed writing of decimal places of amount and amount checksum (according to DTAUS documentation)
 *           - fixed empty DM-Amount-Field: must be 0 instead of blank.
 *           - added selection of transaction type: Direct debit or bank transfer
 *           - added sample code
 */

using System;
using System.Linq;
using System.Collections.Specialized;
using System.Text;
using System.Collections.Generic;

namespace Ict.Plugin.EP.Germany
{
    /// <summary>
    /// States the type of Transaction for a BankTransactionRequest.
    /// </summary>
    public enum DTAUSTransactionType
    {
        /// <summary>
        /// Unknown type, this will fail when calling WriteDTAUSFile...
        /// </summary>
        Unknown,
        /// <summary>
        /// Direct debit request, the executive account will receive the money from the target accounts (Lastschrift-Einzug).
        /// </summary>
        DirectDebit,
        /// <summary>
        /// regular bank transfer, the executive account will transfer the money to the target accounts (Überweisung).
        /// </summary>
        BankTransfer
    }

    /// <summary>
    /// Container for bank transaction requests. This object can be used together with the DTAUSWriter class in order to
    /// create a DTAUS file for bank transactions.
    /// </summary>
    /// <author>Florian Harbich</author>
    /// <created>21.11.2009 16:11</created>
    /// <version>$Revision:$</version>
    /// <id>$Id:$</id>
    public class BankTransactionRequest
    {
        public DTAUSTransactionType TransactionType { get; set; }

        /// <summary>
        /// List of all concrete direct debit transactions for this request.
        /// </summary>
        public List<BankTransactionItem> Transactions { get; private set; }

        /// <summary>
        /// Gets or sets the creation date of this request structure.
        /// </summary>
        public DateTime CreationDate { get; set; }

        /// <summary>
        /// Gets or sets the requested execution date of the direct debit transactions.
        /// For use with DTAUS files, this date must not be less than the creation date and not more than 15 days after the creation date.
        /// Can be null to indicate immediate execution.
        /// </summary>
        public DateTime? ExecutionDate { get; set; }

        /// <summary>
        /// Gets or sets the bank number (BLZ) from which the transaction originates.
        /// </summary>
        public int ExecutiveBankNumber { get; set; }

        /// <summary>
        /// Gets or sets the bank account number from which thetransaction originates.
        /// </summary>
        public int ExecutiveBankAccountNumber { get; set; }

        /// <summary>
        /// Gets or sets the bank account holder from which the direct debit originates.
        /// </summary>
        public string ExecutiveBankAccountHolder { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="BankTransactionRequest"/> class.
        /// </summary>
        public BankTransactionRequest()
        {
            this.Transactions = new List<BankTransactionItem>();
            this.TransactionType = DTAUSTransactionType.Unknown;
        }
    }

    /// <summary>
    /// Container for bank transactions. This object can be used in BankTransactionRequest objects
    /// together with the DTAUSWriter class in order to
    /// create a DTAUS file for bank transactions.
    /// </summary>
    /// <author>Florian Harbich</author>
    /// <created>21.11.2009 16:15</created>
    /// <version>$Revision:$</version>
    /// <id>$Id:$</id>
    public class BankTransactionItem
    {
        /// <summary>
        /// List of Reference lines (Verwendungszweck) for this transaction.
        /// At least one line but not more than 14 are needed for use with DTAUS files.
        /// </summary>
        public List<string> ReferenceLines { get; private set; }

        /// <summary>
        /// Gets or sets the bank number (BLZ) of the recipient account.
        /// </summary>
        public int TargetBankNumber { get; set; }

        /// <summary>
        /// Gets or sets the bank account number of the recipient account.
        /// </summary>
        public long TargetBankAccountNumber { get; set; }

        /// <summary>
        /// Gets or sets the bank account holder of the account stated in TargetBankAccountNumber
        /// (the recipient account).
        /// </summary>
        public string TargetBankAccountHolder { get; set; }

        /// <summary>
        /// Gets or sets the amount of money that is requested from the TargetBankAccount in Euro-Cents.
        /// For DTAUS files, this value must not be 0 or negative.
        /// </summary>
        public int AmountCents { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="BankTransactionItem"/> class.
        /// </summary>
        public BankTransactionItem()
        {
            this.ReferenceLines = new List<string>();
        }
    }


    /// <summary>
    /// This class writes DTAUS files for german banks, direct debit orders
    /// see http://www.infodrom.org/projects/dtaus/dtaus.html for description of file format;
    /// the data comes from an xml file
    /// </summary>
    public static class DTAUSWriter
    {
        /// <summary>
        /// States the alignment for length ensured strings.
        /// </summary>
        private enum Alignment
        {
            /// <summary>
            /// The string is left-aligned, missing characters are added to the right.
            /// </summary>
            AlignLeft,
            /// <summary>
            /// The string is right-aligned, missing characters are added to the left.
            /// </summary>
            AlignRight
        };

        /// <summary>
        /// The Encoding used for DTAUS files.
        /// </summary>
        public static Encoding DTAUSFileEncoding = Encoding.ASCII;

        /// <summary>
        /// Creates a complete DTAUS data string for the given request object.
        /// </summary>
        /// <param name="request">The request to create the DTAUS dataset from. must not be null.</param>
        /// <returns>the complete DTAUS dataset for the given request as an ascii byte[].</returns>
        /// <exception cref="InvalidOperationException">When any data in the request is invalid (bad string length etc.),
        /// an InvalidOperationException is created. See its message for detailed information.</exception>
        /// <exception cref="ArgumentOutOfRangeException">The request object must contain at least one transaction,
        /// else an ArgumentOutOfRangeException is thrown.</exception>
        /// <exception cref="NotSupportedException">The TransactionType of the request is not supported.</exception>
        public static byte[] WriteDTAUSFile(BankTransactionRequest request)
        {
            StringBuilder result = new StringBuilder();

            if (request.Transactions.Count < 1)
            {
                throw new ArgumentOutOfRangeException("request", "the Direct debit request must contain at least one transaction object.");
            }

            
            WriteHeader(result, request);

            foreach (BankTransactionItem transaction in request.Transactions)
            {
                WriteTransactionBody(result, transaction, request);
            }
            
            WriteFooter(result, request);

            return DTAUSFileEncoding.GetBytes(result.ToString());
        }

        #region internal helpers

        /// <summary>
        /// Checks if the length of the input string is exactly the required length. 
        /// </summary>
        /// <param name="input">The input string to check.</param>
        /// <param name="requiredlength">The required length of the input string.</param>
        /// <param name="msg">An error message to be created when the length does not match.</param>
        /// <exception cref="InvalidOperationException">When the input string length does not math the required length,
        /// an InvalidOperationException is created. See its message for detailed information.</exception>
        private static void CheckLength(string input, int requiredlength, string msg)
        {
            if (input.Length != requiredlength)
            {
                throw new InvalidOperationException("wrong length: " + msg);
            }
        }

        /// <summary>
        /// Formats the given input string such that
        /// * it uses only uppercase letters
        /// * it is not null and has no blanks at the beginning or end
        /// * it contains only valid characters for DTAUS files.
        /// </summary>
        /// <param name="input">The input to format, may be null.</param>
        /// <returns>the formatted input as described in the summary. never null, may be empty.</returns>
        public static string FormatName(string input)
        {
            string allowedchars = " 0123456789.,&-/+*$%ABCDEFGHIJKLMNOPQRSTUVWXYZ";

            input = (input ?? String.Empty).Trim().ToUpper().
              Replace("Ü", "UE").
              Replace("Ä", "AE").
              Replace("Ö", "OE").
              Replace("ß", "SS");

            for (int count = 0; count < input.Length; count++)
            {
                if (allowedchars.IndexOf(input[count]) < 0)
                {
                    input = input.Substring(0, count) + ' ' + input.Substring(count + 1);
                }
            }

            return input;
        }

        /// <summary>
        /// Returns a string whose length is exactly requredlength characters.
        /// </summary>
        /// <param name="input">The input string to pad up to the required length.</param>
        /// <param name="requiredlength">The length of the string to be returned.</param>
        /// <param name="fieldname">The fieldname (only used in error message when the input string is too long).</param>
        /// <param name="fill">The character used to pad the input string to the required length.</param>
        /// <param name="alignwhere">The Alignment of the padded string.</param>
        /// <returns>A string containing the input string with length requiredlength. missing characters from
        /// input are padded using the fill character such that the input string is aligned acccording to the alignwhere parameter.</returns>
        ///<exception cref="InvalidOperationException">The input length is longer than the requiredlength parameter.</exception>
        private static string EnsureLength(string input, int requiredlength, string fieldname, char fill, Alignment alignwhere)
        {
            string padded;

            if (input.Length > requiredlength)
            {
                throw new InvalidOperationException("Problem with length of " + fieldname + " " +
                                    input + "; should only be " + requiredlength.ToString());
            }
            else if (input.Length < requiredlength)
            {
                if (alignwhere == Alignment.AlignRight)
                {
                    padded = input.PadLeft(requiredlength, fill);
                }
                else
                {
                    padded = input.PadRight(requiredlength, fill);
                }
            }
            else
            {
                padded = input;
            }

            return padded;
        }

        #endregion        

        /// <summary>
        /// Writes the DTAUS dataset A (DTAUS Header)
        /// </summary>
        /// <param name="result">The StringBuilder to write the header to.</param>
        /// <param name="request">The Direct debit request object for which the header is created.</param>
        ///<exception cref="InvalidOperationException">The execution date of the request is before the creation date or more than 15 days after the creation date. </exception>
        /// <exception cref="NotSupportedException">The TransactionType is not supported.</exception>
        private static void WriteHeader(StringBuilder result, BankTransactionRequest request)
        {
            DateTime creationDate = request.CreationDate.Date;
            DateTime? dateEffective = request.ExecutionDate;

            #region sanity checks

            if (String.IsNullOrEmpty(request.ExecutiveBankAccountHolder))
            {
                throw new InvalidOperationException("No executive account holder is given for this transaction.");
            }

            if (request.ExecutiveBankAccountNumber <= 0)
            {
                throw new InvalidOperationException("The executive bank account number must not be 0 or negative!.");
            }

            if (request.ExecutiveBankNumber < 10000000 || request.ExecutiveBankNumber >= 90000000)
            {
                throw new InvalidOperationException("The executive bank number is invalid.");
            }

            if (dateEffective.HasValue)
            {
                double daysBetween = (dateEffective.Value.Date - creationDate).TotalDays;

                if (daysBetween < 0.0 || daysBetween > 15.0)
                {
                    throw new InvalidOperationException("Date of execution must not be before today and no more than 15 days from today!");
                }
            }

            #endregion

            /* DTAUS Dataset A Structure (see http://www.infodrom.org/projects/dtaus/dtaus.html)
            
            1 	0 	 4 Zeichen 	 Länge des Datensatzes, immer 128 Bytes, also immer "0128"
            2 	4 	 1 Zeichen 	 Datensatz-Typ, immer 'A'
            3 	5 	 2 Zeichen 	 Art der Transaktionen
                                "LB" für Lastschriften Bankseitig
                                "LK" für Lastschriften Kundenseitig
                                "GB" für Gutschriften Bankseitig
                                "GK" für Gutschriften Kundenseitig
            4 	7 	 8 Zeichen 	 Bankleitzahl des Auftraggebers
            5 	15 	 8 Zeichen 	 CST, "00000000", nur belegt, wenn Diskettenabsender Kreditinstitut
            6 	23 	 27 Zeichen 	 Name des Auftraggebers
            7 	50 	 6 Zeichen 	 aktuelles Datum im Format DDMMJJ
            8 	56 	 4 Zeichen 	 CST, "    " (Blanks)
            9 	60 	 10 Zeichen 	 Kontonummer des Auftraggebers
            10 	70 	 10 Zeichen 	 Optionale Referenznummer (numerisch, leer = 10*'0')
            11a 	80 	 15 Zeichen 	 Reserviert, 15 Blanks
            11b 	95 	 8 Zeichen 	 Optionales Ausführungsdatum im Format DDMMJJJJ. Nicht jünger als Erstellungsdatum (A7), jedoch höchstens 15 Kalendertage später. Sonst Blanks.
            11c 	103 	 24 Zeichen 	 Reserviert, 24 Blanks
            12 	127 	 1 Zeichen 	 Währungskennzeichen
                                    " " = DM
                                    "1" = Euro
                    Insgesamt 128 Zeichen      
            */

            result.Append("0128");
            result.Append("A");
            if (request.TransactionType == DTAUSTransactionType.DirectDebit)
            {
                result.Append("LK");
            }
            else if (request.TransactionType == DTAUSTransactionType.BankTransfer)
            {
                result.Append("GK");
            }
            else
            {
                throw new NotSupportedException("The Transaction type " + request.TransactionType + " is not yet supported.");
            }
            result.Append(EnsureLength(request.ExecutiveBankNumber.ToString(), 8, "ExecutiveBankSortcode", '0', Alignment.AlignLeft));
            result.Append(new String('0', 8));
            result.Append(EnsureLength(FormatName(request.ExecutiveBankAccountHolder), 27, "ExecutiveName", ' ', Alignment.AlignLeft));
            result.AppendFormat("{0:ddMMyy}", creationDate);
            result.Append(new String(' ', 4));
            result.Append(EnsureLength(request.ExecutiveBankAccountNumber.ToString(), 10, "ExecutiveAccountNumber", '0', Alignment.AlignRight));
            result.Append(new String('0', 10));
            result.Append(new String(' ', 15));
            if (dateEffective.HasValue)
            {
                result.AppendFormat("{0:ddMMyyyy}", dateEffective.Value);
            }
            else
            {
                result.Append(new String(' ', 8));
            }
            result.Append(new String(' ', 24));
            result.Append("1");
        }

        /// <summary>
        /// Writes a DTAUS dataset C block (Body) for the given direct debit request and transaction object.
        /// </summary>
        /// <param name="result">The StringBuilder to write the transaction to.</param>
        /// <param name="transaction">The transaction to be written</param>
        /// <param name="request">The request object that contains the direct debit recipient information.</param>
        /// <exception cref="InvalidOperationException">When any data in the request is invalid (bad string length etc.),
        /// an InvalidOperationException is created. See its message for detailed information.</exception>
        /// <exception cref="NotSupportedException">The TransactionType is not supported.</exception>
        private static void WriteTransactionBody(StringBuilder result, BankTransactionItem transaction, BankTransactionRequest request)
        {
            List<string> extraReferences = transaction.ReferenceLines;

            #region sanity checks

            if (String.IsNullOrEmpty(transaction.TargetBankAccountHolder))
            {
                throw new InvalidOperationException("No target account holder is given for this transaction.");
            }

            if (transaction.TargetBankAccountNumber <= 0)
            {
                throw new InvalidOperationException("target account number must not be 0 or negative!.");
            }

            if (transaction.TargetBankNumber < 10000000 || transaction.TargetBankNumber >= 90000000)
            {
                throw new InvalidOperationException("target bank number is invalid.");
            }

            if (extraReferences.Count < 1)
            {
                throw new InvalidOperationException("No Reference text is given for this transaction.");
            }

            if (transaction.AmountCents <= 0)
            {
                throw new InvalidOperationException("The amount for each transaction must be greater than 0");
            }

            if (extraReferences.Count > 14)
            {
                throw new InvalidOperationException("at most 14 reference lines can be given for each transaction.");
            }

            #endregion

            string primaryReferenceLine = extraReferences[0];
            extraReferences.RemoveAt(0);

            /* Aufbau Datensatz C
                Nr.	    Start	Länge	Beschreibung
                1	    0	 4 Zeichen	 Länge des Datensatzes, 187 + x * 29 (x..Anzahl Erweiterungsteile)
                2	    4	 1 Zeichen	 Datensatz-Typ, immer 'C'
                3	    5	 8 Zeichen	 Bankleitzahl des Auftraggebers (optional, ggf. '0' auffüllen)
                4	    13	 8 Zeichen	 Bankleitzahl des Kunden
                5	    21	 10 Zeichen	 Kontonummer des Kunden
                6	    31	 13 Zeichen	 Verschiedenes 
                                         1. Zeichen: "0" 
                                         2. - 12. Zeichen: interne Kundennummer oder Nullen 
                                         13. Zeichen: "0" 
                                         Die interne Nummer wird vom erstbeauftragten Institut zum endbegünstigten Institut weitergeleitet. Die Weitergabe der internenen Nummer an den Überweisungsempfänger ist der Zahlstelle freigestellt. 
                7	    44	 5 Zeichen	 Art der Transaktion (7a: 2 Zeichen, 7b: 3 Zeichen) 
                                         "04000" Lastschrift des Abbuchungsauftragsverfahren 
                                         "05000" Lastschrift des Einzugsermächtigungsverfahren 
                                         "05005" Lastschrift aus Verfügung im elektronischen Cash-System 
                                         "05006" Wie 05005 mit ausländischen Karten 
                                         "05015" Lastschrift aus Verfügung im elec. Cash-System - POZ 
                                         "51000" Überweisungs-Gutschrift 
                                         "53000" Überweisung Lohn/Gehalt/Rente 
                                         "54XXJ" Vermögenswirksame Leistung (VL) mit Sparzulage 
                                         "56000" Überweisung öffentlicher Kassen 
                                         Die im Textschlüssel mit XX bezeichnete Stelle ist 00 oder der Prozentsatz der Sparzulage. 
                                         Die im Textschlüssel mit J bezeichnete Stelle wird bei Übernahme in eine Zahlung automatisch mit der jeweils aktuellen Jahresendziffer (z.B. 7, wenn 97) ersetzt. 
                8	    49	 1 Zeichen	 Reserviert, " " (Blank)
                9	    50	 11 Zeichen	 Betrag (9 Vorkomma, 2 Nachkommastellen ohne Trennzeichen)
                10	    61	 8 Zeichen	 Bankleitzahl des Auftraggebers
                11    	69	 10 Zeichen	 Kontonummer des Auftraggebers
                12    	79	 11 Zeichen	 Betrag in Euro (9 Vorkomma, 2 Nachkommastellen ohne Trennzeichen), nur belegt, wenn Euro als Währung angegeben wurde (A12, C17a), sonst Nullen
                13    	90	 3 Zeichen	 Reserviert, 3 Blanks
                14a   	93	 27 Zeichen	 Name des Kunden
                14b	   120	 8 Zeichen	 Reserviert, 8 Blanks
 	 	        Insgesamt 128 Zeichen
                15	   128	 27 Zeichen	 Name des Auftraggebers
                16	   155	 27 Zeichen	 Verwendungszweck
                17a	   182	 1 Zeichen	 Währungskennzeichen 
                                         " " = DM 
                                         "1" = Euro 
                17b    183	 2 Zeichen	 Reserviert, 2 Blanks
                18	   185	 2 Zeichen	 Anzahl der Erweiterungsdatensätze, "00" bis "15"
                19	   187	 2 Zeichen	 Typ (1. Erweiterungsdatensatz) 
                                         "01" Name des Kunden 
                                         "02" Verwendungszweck 
                                         "03" Name des Auftraggebers 
                20     189	 27 Zeichen	 Beschreibung gemäß Typ
                21	   216	 2 Zeichen	 wie C19, oder Blanks (2. Erweiterungsdatensatz)
                22	   218	 27 Zeichen	 wie C20, oder Blanks
                23	   245	 11 Zeichen	 11 Blanks
 	 	        Insgesamt 256 Zeichen
             */

            result.AppendFormat("{0:0000}", 187 + (extraReferences.Count * 29));
            result.Append("C");
            result.Append(new String('0', 8));
            result.Append(EnsureLength(transaction.TargetBankNumber.ToString(), 8, "PartnerBankSortcode", '0', Alignment.AlignRight));
            result.Append(EnsureLength(transaction.TargetBankAccountNumber.ToString(), 10, "PartnerBankAccountNumber", '0', Alignment.AlignRight));
            result.Append("0");
            result.Append(new String('0', 11));
            result.Append("0");
            if (request.TransactionType == DTAUSTransactionType.DirectDebit)
            {
                result.Append("05000"); // Einzugsermaechtigung
            }
            else if (request.TransactionType == DTAUSTransactionType.BankTransfer)
            {
                result.Append("51000"); // Überweisungs-Gutschrift
            }
            else
            {
                throw new NotSupportedException("The Transaction type " + request.TransactionType + " is not yet supported.");
            }
            result.Append(" "); 
            result.Append(new String('0', 11)); // With Euro-Transactions, the Amount is in Field C12, not here.
            result.Append(EnsureLength(request.ExecutiveBankNumber.ToString(), 8, "ExecutiveBankSortcode", '0', Alignment.AlignRight));
            result.Append(EnsureLength(request.ExecutiveBankAccountNumber.ToString(), 10, "ExecutiveBankAccountNumber", '0', Alignment.AlignRight));
            
            string amountFormatted = Convert.ToDouble(transaction.AmountCents).ToString(new String('0', 11));
            CheckLength(amountFormatted, 11, "The Amount of this transaction is too big to be used in a DTA file.");
            result.Append(amountFormatted);
            result.Append(new String(' ', 3));

            result.Append(EnsureLength(FormatName(transaction.TargetBankAccountHolder), 27, "PartnerName", ' ', Alignment.AlignLeft));
            result.Append(new String(' ', 8));

            result.Append(EnsureLength(FormatName(request.ExecutiveBankAccountHolder), 27, "ExecutiveName", ' ', Alignment.AlignLeft));
            result.Append(EnsureLength(FormatName(primaryReferenceLine), 27, "PartnerKeyReference", ' ', Alignment.AlignLeft));
            result.Append("1");
            result.Append(new string(' ', 2));

            WriteAllReferenceExtensions(result, extraReferences);
        }

        /// <summary>
        /// Writes all extra reference lines as extension blocks to the given StringBuilder.
        /// </summary>
        /// <param name="result">The Stringbuilder to write to.</param>
        /// <param name="references">The list of reference lines to be written. must not be more than 13.</param>
        /// <exception cref="InvalidOperationException">When more than 13 reference lines are given,
        /// an InvalidOperationException is created.</exception>
        private static void WriteAllReferenceExtensions(StringBuilder result, List<string> references)
        {
            if (references.Count > 13)
            {
                throw new InvalidOperationException("A Transaction can have at most 13 additional reference lines.");
            }

            //First add the yet missing extension count (Field C18).
            result.AppendFormat("{0:00}", references.Count);            

            if (references.Count > 0)
            {
                //The first two extensions will complete the 128 characters of the dataset C base block: 2*(2+27) + 11 chars                
                for (int i = 0; i < 2; i++)
			    {
                    string reference = null;

                    if (i < references.Count)
                    {
                        reference = references[i];
                    }

			        WriteReferenceExtension(result, reference);
			    }
                result.Append(new String(' ', 11));

                //The remaining extensions (#<=13) must be written in blocks of 4 extensions with a total of 128 chars.
                //4*(2+27) + 12
                //the last extension block can have only one extension (because the total is 15), but must still be
                //128 chars wide, so the remainder is simply filled with empty blocks.
                //Because the references list is checked to have less than 14 items, the maximum block count is always ensured.
                for (int block = 0; block < 5; block++)
			    {
                    //first block of four starts with the third extension (index 2) and so on
                    int offset = (4*block) + 2;

                    if (offset < references.Count)
                    {                
                        //4 extensions will form a 128 chars block and always have to be written together.
                        //if not enough extensions are available, fill the remaining block with blanks.
                        for (int i = 0; i < 4 ; i++)
                        {
                            int currentIndex = offset + i;
                            string reference = null;
                            
                            if (currentIndex < references.Count)
                            {
                                reference = references[currentIndex];
                            }

			                WriteReferenceExtension(result, reference);
                        }
                        result.Append(new String(' ', 12));
                    }
                    else
                    {
                        //no more extensions, so do not start a new 128 chars block and terminate extension writing.
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// Creates the datafields for a reference extension of a dataset C extension block.
        /// when the input string is null or empty, an empty extension is written.
        /// </summary>
        /// <param name="result">The Stringbuilder where the extension is written to.</param>
        /// <param name="referenceLine">The reference line to be written.</param>
        ///<exception cref="InvalidOperationException">The length of the (DTA formatted) reference line is longer than the allowed field size.</exception>
        private static void WriteReferenceExtension(StringBuilder result, string referenceLine)
        {
            //TODO: change signature to DTAExtensionData{ExtensionType type, string contents} and be able to write the extra executive and target name fields, too
            //A check would be needed to ensure that at most 1 type 00 and type 01 extension is defined and
            //the BankTransactionRequest/BankTransactionItem classes must be extended to Support the property "ExtraAccountHolderText"
            if (!String.IsNullOrEmpty(referenceLine))
            {
                result.Append("02"); //C19: 02 = Verwendungszweck
                result.Append(EnsureLength(FormatName(referenceLine), 27, "Extension", ' ', Alignment.AlignLeft));
            }
            else
            {
                result.Append(new string(' ', 2 + 27));
            }
        }

        /// <summary>
        /// Writes the footer (DTAUS dataset E) of the given direct debit request.
        /// </summary>
        /// <param name="result">The StringBuilder to write to.</param>
        /// <param name="request">The request of which the footer is generated.</param>
        /// <exception cref="InvalidOperationException">When any checksum field lengths exceed the allowed field length,
        /// an InvalidOperationException is created. See its message for detailed information.</exception>
        private static void WriteFooter(StringBuilder result, BankTransactionRequest request)
        {
            /* DTAUS Datensatz E (Footer)
            1 	0 	 4 Zeichen 	 Länge des Datensatzes, immer 128 Bytes, also immer "0128"
            2 	4 	 1 Zeichen 	 Datensatz-Typ, immer 'E'
            3 	5 	 5 Zeichen 	 "     " (Blanks)
            4 	10 	 7 Zeichen 	 Anzahl der Datensätze vom Typ C
            5 	17 	 13 Zeichen 	 Kontrollsumme Beträge in DM (11 Vorkommastellen und 2 Nachkommastellen ohne Trennzeichen)
            6 	30 	 17 Zeichen 	 Kontrollsumme Kontonummern
            7 	47 	 17 Zeichen 	 Kontrollsumme Bankleitzahlen
            8 	64 	 13 Zeichen 	 Kontrollsumme Beträge in Euro (11 Vorkommastellen und 2 Nachkommastellen ohne Trennzeichen), nur belegt, wenn Euro als Währung angegeben wurde (A12, C17a)
            9 	77 	 51 Zeichen 	 51 Blanks
                    Insgesamt 128 Zeichen
            */
            string bankAccountNumberSumFormatted = request.Transactions.Sum(item => item.TargetBankAccountNumber).ToString(new String('0', 17));
            CheckLength(bankAccountNumberSumFormatted, 17, "The bank account number sum of this request is too big to be used in a DTA file. Try using less transactions.");

            string bankNumberSumFormatted = request.Transactions.Sum(item => (long)item.TargetBankNumber).ToString(new String('0', 17));
            CheckLength(bankNumberSumFormatted, 17, "The bank number sum of this transaction is too big to be used in a DTA file. Try using less transactions.");
            
            string amountSumFormatted = request.Transactions.Sum(item => item.AmountCents).ToString(new String('0', 13));
            CheckLength(amountSumFormatted, 13, "The total Amount of all transactions in this request is too big to be used in a DTA file. try using less transactions.");

            string transactionCountFormatted = request.Transactions.Count.ToString(new String('0', 7));
            CheckLength(transactionCountFormatted, 7, "The total transaction count is too big to be used in a single DTA file. try using less transactions.");

            result.Append("0128");
            result.Append("E");
            result.Append(new String(' ', 5));
            result.Append(transactionCountFormatted);
            result.Append(new String('0', 13));            
            result.Append(bankAccountNumberSumFormatted);
            result.Append(bankNumberSumFormatted);
            result.Append(amountSumFormatted);
            result.Append(new String(' ', 51));
        }

        #region Sample usage

        /// <summary>
        /// Sample usage method that donates the given amount to the class extending developer Florian Harbich.
        /// </summary>
        /// <param name="accountHolder">The account holder (you).</param>
        /// <param name="accountNumber">The account number (your bank account number).</param>
        /// <param name="bankNumber">The bank number (your bank number (BLZ)).</param>
        /// <param name="amountCents">The amount you would like to donate, in cents.</param>
        /// <returns>byte[] containing the DTAUS file to donate some money to me.</returns>
        public static byte[] DonateToDeveloper(string accountHolder, int accountNumber, int bankNumber, int amountCents)
        {
            BankTransactionRequest request = new BankTransactionRequest()
            {
                CreationDate = DateTime.Now,
                ExecutionDate = null,
                ExecutiveBankAccountHolder = accountHolder,
                ExecutiveBankAccountNumber = accountNumber,
                ExecutiveBankNumber = bankNumber,
                TransactionType = DTAUSTransactionType.BankTransfer
            };

            BankTransactionItem transaction = new BankTransactionItem()
            {
                AmountCents = amountCents,
                TargetBankAccountHolder = "Florian Harbich",
                TargetBankAccountNumber = 834915,
                TargetBankNumber = 60450050,
            };

            transaction.ReferenceLines.Add("Donation for DTAUS");
            transaction.ReferenceLines.Add("writing library");
            transaction.ReferenceLines.Add("in csharp");
            transaction.ReferenceLines.Add("from " + accountHolder);
            
            request.Transactions.Add(transaction);

            return DTAUSWriter.WriteDTAUSFile(request);
        }

        #endregion
    }
}

