// Country specific IBAN data.
// name      - Name of the country
// code      - Country Code from ISO 3166
// bank_form - Format of bank/branch code part (e.g. "0 4a 0 ")
// acc_form  - Format of account number part (e.g. "0  11  2n")
function Country(name, code, bank_form, acc_form) {
  this.name      = name;
  this.code      = code;
  this.bank      = Country_decode_format(bank_form);
  this.acc       = Country_decode_format(acc_form);
  this.bank_lng  = Country_calc_length(this.bank);
  this.acc_lng   = Country_calc_length(this.acc);
  this.total_lng = 4 + this.bank_lng + this.acc_lng;
}

function Country_decode_format(form) {
  var form_list = new Array();
  var parts = form.split(" ");
  for (var i = 0; i < parts.length; ++i)
  {
    var part = parts[i];
    if (part != "")
    {
      var typ = part.charAt(part.length - 1);
      if (typ == "a" || typ == "n")
        part = part.substring(0, part.length - 1);
      else
        typ = "c";
      var lng = parseInt(part);
      form_list[form_list.length] = new Array(lng, typ);
    }
  }
  return form_list;
}

function Country_calc_length(form_list) {
  var sum = 0;
  for (var i = 0; i < form_list.length; ++i)
    sum += form_list[i][0];
  return sum;
}

// Search the country code in the iban_data list.
function CountryData(code)
{
  for (var i = 0; i < iban_data.length; ++i)
    if (iban_data[i].code == code)
      return iban_data[i];
  return null;
}


// BBAN data from ISO 13616, Country codes from ISO 3166 (www.iso.org).
var iban_data = new Array(
  new Country("Andorra",        "AD", "0  4n 4n", "0  12   0 "),
  new Country("Albania",        "AL", "0  8n 0 ", "0  16   0 "),
  new Country("Austria",        "AT", "0  5n 0 ", "0  11n  0 "),
  new Country("Bosnia and Herzegovina",
                                "BA", "0  3n 3n", "0   8n  2n"),
  new Country("Belgium",        "BE", "0  3n 0 ", "0   7n  2n"),
  new Country("Bulgaria",       "BG", "0  4a 4n", "2n  8   0 "),
  new Country("Switzerland",    "CH", "0  5n 0 ", "0  12   0 "),
  new Country("Cyprus",         "CY", "0  3n 5n", "0  16   0 "),
  new Country("Czech Republic", "CZ", "0  4n 0 ", "0  16n  0 "),
  new Country("Germany",        "DE", "0  8n 0 ", "0  10n  0 "),
  new Country("Denmark",        "DK", "0  4n 0 ", "0   9n  1n"),
  new Country("Estonia",        "EE", "0  2n 0 ", "2n 11n  1n"),
  new Country("Spain",          "ES", "0  4n 4n", "2n 10n  0 "),
  new Country("Finland",        "FI", "0  6n 0 ", "0   7n  1n"),
  new Country("Faroe Islands",  "FO", "0  4n 0 ", "0   9n  1n"),
  new Country("France",         "FR", "0  5n 5n", "0  11   2n"),
  new Country("United Kingdom", "GB", "0  4a 6n", "0   8n  0 "),
  new Country("Gibraltar",      "GI", "0  4a 0 ", "0  15   0 "),
  new Country("Greenland",      "GL", "0  4n 0 ", "0   9n  1n"),
  new Country("Greece",         "GR", "0  3n 4n", "0  16   0 "),
  new Country("Croatia",        "HR", "0  7n 0 ", "0  10n  0 "),
  new Country("Hungary",        "HU", "0  3n 4n", "1n 15n  1n"),
  new Country("Ireland",        "IE", "0  4a 6n", "0   8n  0 "),
  new Country("Israel",         "IL", "0  3n 3n", "0  13n  0 "),
  new Country("Iceland",        "IS", "0  4n 0 ", "2n 16n  0 "),
  new Country("Italy",          "IT", "1a 5n 5n", "0  12   0 "),
  new Country("Liechtenstein",  "LI", "0  5n 0 ", "0  12   0 "),
  new Country("Lithuania",      "LT", "0  5n 0 ", "0  11n  0 "),
  new Country("Luxembourg",     "LU", "0  3n 0 ", "0  13   0 "),
  new Country("Latvia",         "LV", "0  4a 0 ", "0  13   0 "),
  new Country("Monaco",         "MC", "0  5n 5n", "0  11   2n"),
  new Country("Montenegro",     "ME", "0  3n 0 ", "0  13n  2n"),
  new Country("Macedonia, Former Yugoslav Republic of",
                                "MK", "0  3n 0 ", "0  10   2n"),
  new Country("Malta",          "MT", "0  4a 5n", "0  18   0 "),
  new Country("Mauritius",      "MU", "0  4a 4n", "0  15n  3a"),
  new Country("Netherlands",    "NL", "0  4a 0 ", "0  10n  0 "),
  new Country("Norway",         "NO", "0  4n 0 ", "0   6n  1n"),
  new Country("Poland",         "PL", "0  8n 0 ", "0  16n  0 "),
  new Country("Portugal",       "PT", "0  4n 4n", "0  11n  2n"),
  new Country("Romania",        "RO", "0  4a 0 ", "0  16   0 "),
  new Country("Serbia",         "RS", "0  3n 0 ", "0  13n  2n"),
  new Country("Saudi Arabia",   "SA", "0  2n 0 ", "0  18   0 "),
  new Country("Sweden",         "SE", "0  3n 0 ", "0  16n  1n"),
  new Country("Slovenia",       "SI", "0  5n 0 ", "0   8n  2n"),
  new Country("Slovak Republic",
                                "SK", "0  4n 0 ", "0  16n  0 "),
  new Country("San Marino",     "SM", "1a 5n 5n", "0  12   0 "),
  new Country("Tunisia",        "TN", "0  2n 3n", "0  13n  2n"),
  new Country("Turkey",         "TR", "0  5n 0 ", "1  16   0 "));

// Translation table and translation function for localized versions
var trans_tab = new Array();

// Übersetzungstabelle in iban.js füllen
fill_trans_tab(new Array(
  "Andorra",                             "Andorra",
  "Albania",                             "Albanien",
  "Austria",                             "Österreich",
  "Bosnia and Herzegovina",              "Bosnien und Herzegowina",
  "Belgium",                             "Belgien",
  "Bulgaria",                            "Bulgarien",
  "Switzerland",                         "Schweiz",
  "Cyprus",                              "Zypern",
  "Czech Republic",                      "Tschechien",
  "Germany",                             "Deutschland",
  "Denmark",                             "Dänemark",
  "Estonia",                             "Estland",
  "Spain",                               "Spanien",
  "Finland",                             "Finnland",
  "Faroe Islands",                       "Färöer",
  "France",                              "Frankreich",
  "United Kingdom",                      "Vereinigtes Königreich",
  "Gibraltar",                           "Gibraltar",
  "Greenland",                           "Grönland",
  "Greece",                              "Griechenland",
  "Croatia",                             "Kroatien",
  "Hungary",                             "Ungarn",
  "Ireland",                             "Irland",
  "Israel",                              "Israel",
  "Iceland",                             "Island",
  "Italy",                               "Italien",
  "Liechtenstein",                       "Liechtenstein",
  "Lithuania",                           "Litauen",
  "Luxembourg",                          "Luxemburg",
  "Latvia",                              "Lettland",
  "Monaco",                              "Monaco",
  "Montenegro",                          "Montenegro",
  "Macedonia, Former Yugoslav Republic of",
                                 "Mazedonien, Ehemalige Jugoslawische Republik",
  "Malta",                               "Malta",
  "Mauritius",                           "Mauritius",
  "Netherlands",                         "Niederlande",
  "Norway",                              "Norwegen",
  "Poland",                              "Polen",
  "Portugal",                            "Portugal",
  "Romania",                             "Rumänien",
  "Serbia",                              "Serbien",
  "Saudi Arabia",                        "Saudi-Arabien",
  "Sweden",                              "Schweden",
  "Slovenia",                            "Slowenien",
  "Slovak Republic",                     "Slowakische Republik",
  "San Marino",                          "San Marino",
  "Tunisia",                             "Tunesien",
  "Turkey",                              "Türkei",
  "Unknown Country Code: ",              "Unbekannter Länder-Code: ",
  "Bank/Branch Code length ",            "Länge der Bankleitzahl ",
  " is not correct for ",                " nicht korrekt für ",
  "Bank/Branch Code ",                   "Bankleitzahl ",
  "Account Number length ",              "Länge der Kontonummer ",
  "Account Number ",                     "Kontonummer ",
  "IBAN length ",                        "IBAN-Länge ",
  "Checksum of IBAN incorrect",          "IBAN-Checksumme nicht korrekt",
  "Country",                             "Länder-",
  "Code",                                "Code",
  "Bank/Branch Code",                    "Bankleitzahl",
  "Account Number",                      "Kontonummer",
  "check1",                              "Prüf1",
  "bank",                                "Bank",
  "branch",                              "Zweigstelle",
  "check2",                              "Prüf2",
  "number",                              "Nummer",
  "check3",                              "Prüf3",
  "a = A-Z, n = 0-9, c = A-Z/a-z/0-9",   "a = A-Z, n = 0-9, c = A-Z/a-z/0-9",
  "Country Code",                        "Länder-Code",
  "International Bank Account Number",   "Internationale Kontonummer",
  "Checksum",                            "Checksumme",
  "Unknown Country Code",                "Unbekannter Länder-Code",
  "Incorrect Bank/Branch Code",          "Bankleitzahl nicht korrekt",
  "Incorrect Account Number",            "Kontonummer nicht korrekt",
  "Country code changed",                "Länder-Code verändert",
  "Incorrect IBAN length: ",             "IBAN-Länge nicht korrekt",
  "Bank/Branch Code changed",            "Bankleitzahl verändert",
  "Account Number changed",              "Kontonummer verändert",
  "Incorrect checksum",                  "Checksumme nicht korrekt",
  "Checksum changed",                    "Checksumme verändert"));
   
// Modulo 97 for huge numbers given as digit strings.
function mod97(digit_string)
{
  var m = 0;
  for (var i = 0; i < digit_string.length; ++i)
    m = (m * 10 + parseInt(digit_string.charAt(i))) % 97;
  return m;
}

// Convert a capital letter into digits: A -> 10 ... Z -> 35 (ISO 13616).
function capital2digits(ch)
{
  var capitals = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  for (var i = 0; i < capitals.length; ++i)
    if (ch == capitals.charAt(i))
      break;
  return i + 10;
}

// Fill the string with leading zeros until length is reached.
function fill0(s, l)
{
  while (s.length < l)
    s = "0" + s;
  return s;
}

// Compare two strings respecting german umlauts.
function strcmp(s1, s2)
{
  var chars = "AaÄäBbCcDdEeFfGgHhIiJjKkLlMmNnOoÖöPpQqRrSsßTtUuÜüVvWwXxYyZz";
  var lng = (s1.length < s2.length) ? s1.length : s2.length;
  for (var i = 0; i < lng; ++i)
  {
    var d = chars.indexOf(s1.charAt(i)) - chars.indexOf(s2.charAt(i));
    if (d != 0)
      return d;
  }
  return s1.length - s2.length;
}

// Create an index table of the iban_data list sorted by country names.
function CountryIndexTable()
{
  var tab = new Array();
  var i, j, t;
  for (i = 0; i < iban_data.length; ++i)
    tab[i] = i;
  for (i = tab.length - 1; i > 0; --i)
    for (j = 0; j < i; ++j)
      if (strcmp(iban_data[tab[j]].name, iban_data[tab[j+1]].name) > 0)
        t = tab[j], tab[j] = tab[j+1], tab[j+1] = t;
  return tab;
}

// Calculate 2-digit checksum of an IBAN.
function ChecksumIBAN(iban)
{
  var code     = iban.substring(0, 2);
  var checksum = iban.substring(2, 4);
  var bban     = iban.substring(4);

  // Assemble digit string
  var digits = "";
  for (var i = 0; i < bban.length; ++i)
  {
    var ch = bban.charAt(i).toUpperCase();
    if ("0" <= ch && ch <= "9")
      digits += ch;
    else
      digits += capital2digits(ch);
  }
  for (var i = 0; i < code.length; ++i)
  {
    var ch = code.charAt(i);
    digits += capital2digits(ch);
  }
  digits += checksum;

  // Calculate checksum
  checksum = 98 - mod97(digits);
  return fill0("" + checksum, 2);
}

// Fill the account number part of IBAN with leading zeros.
function FillAccount(country, account)
{
  return fill0(account, country.acc_lng);
}

// Check if syntax of the part of IBAN is invalid.
function InvalidPart(form_list, iban_part)
{
  for (var f = 0; f < form_list.length; ++f)
  {
    var lng = form_list[f][0], typ = form_list[f][1];
    if (lng > iban_part.length)
      lng = iban_part.length;
    for (var i = 0; i < lng; ++i)
    {
      var ch = iban_part.charAt(i);
      var a = ("A" <= ch && ch <= "Z");
      var n = ("0" <= ch && ch <= "9");
      var c = n || a || ("a" <= ch && ch <= "z");
      if ((!c && typ == "c") || (!a && typ == "a") || (!n && typ == "n"))
        return true;
    }
    iban_part = iban_part.substring(lng);
  }
  return false;
}

// Check if length of the bank/branch code part of IBAN is invalid.
function InvalidBankLength(country, bank)
{
  return (bank.length != country.bank_lng);
}

// Check if syntax of the bank/branch code part of IBAN is invalid.
function InvalidBank(country, bank)
{
  return (InvalidBankLength(country, bank) ||
          InvalidPart(country.bank, bank));
}

// Check if length of the account number part of IBAN is invalid.
function InvalidAccountLength(country, account)
{
  return (account.length < 1 || account.length > country.acc_lng);
}

// Check if syntax of the account number part of IBAN is invalid.
function InvalidAccount(country, account)
{
  return (InvalidAccountLength(country, account) ||
          InvalidPart(country.acc, FillAccount(country, account)));
}

// Check if length of IBAN is invalid.
function InvalidIBANlength(country, iban)
{
  return (iban.length != country.total_lng);
}

// Convert iban from intern value to string format (IBAN XXXX XXXX ...).
function extern(intern)
{
  var s = "IBAN";
  for (var i = 0; i < intern.length; ++i)
  {
    if (i % 4 == 0)
      s += " ";
    s += intern.charAt(i);
  }
  return s;
}

// Convert iban from string format to intern value.
function intern(extern)
{
  if (extern.substring(0, 4) == "IBAN")
    extern = extern.substring(4);
  var s = "";
  for (var i = 0; i < extern.length; ++i)
    if (extern.charAt(i) != " ")
      s += extern.charAt(i);
  return s;
}

// Calculate the checksum and assemble the IBAN.
function CalcIBAN(country, bank, account)
{
  var fill_acc = FillAccount(country, account);
  var checksum = ChecksumIBAN(country.code + "00" + bank + fill_acc);
  return country.code + checksum + bank + fill_acc;
}

function CalcAltIBAN(country, bank, account)
{
  var fill_acc = FillAccount(country, account);
  var checksum = ChecksumIBAN(country.code + "00" + bank + fill_acc);
  checksum = fill0("" + mod97(checksum), 2);
  return country.code + checksum + bank + fill_acc;
}

// Check the checksum of an IBAN.
function IBANokay(iban)
{
  return ChecksumIBAN(iban) == "97";
}

// Check the input, calculate the checksum and assemble the IBAN.
function createIBAN()
{
  var form = document.forms[0];
  var code = form.f_ad_country.options[form.f_ad_country.selectedIndex].value;
  var bank = intern(form.f_cmp_bankno.value);
  var account = intern(form.f_cmp_bankacc.value);
  var country = CountryData(code);

  var err = null, err_focus = null;
  if (country == null)
  {
    err = _("Unbekannter Ländercode: ") + code;
    err_focus = form.f_ad_country;
  }
  else if (InvalidBankLength(country, bank))
  {
    err = _("Die Länge der Bankleitzahl ") + bank.length +
          _(" ist nicht korrekt für ") + country.name +
          " (" + country.bank_lng + ")";
    err_focus = form.f_cmp_bankno;
  }
  else if (InvalidBank(country, bank))
  {
    err = _("Die Bankleitzahl ") + bank + _(" ist nicht korrekt für ") +
          country.name;
    err_focus = form.f_cmp_bankno;
  }
  else if (InvalidAccountLength(country, account))
  {
    err = _("Die Länge der Kontonummer ") + account.length +
          _(" ist nicht korrekt für ") + country.name +
          " (" + country.acc_lng + ")";
    err_focus = form.f_cmp_bankacc;
  }
  else if (InvalidAccount(country, account))
  {
    err = _("Die Kontonummer ") + account + _(" ist nicht korrekt für ") +
          country.name;
    err_focus = form.f_cmp_bankacc;
  }

  if (err)
  {
    err_focus.focus();
    alert(err);
    // form.f_cmp_iban.value = "";
  }
  else
  {
    // Calculate IBAN, write results in form fields
    /*
    form.f_cmp_bankno.value = bank;
    form.f_cmp_bankacc.value = FillAccount(country, account);
    */
    tmpIBAN = extern(CalcIBAN(country, bank, account));
    if (confirm('Möchten Sie die errechnete Nummer ' + tmpIBAN + ' übernehmen?')) {
       form.f_cmp_iban.value = extern(CalcIBAN(country, bank, account));
       form.f_cmp_iban.focus();
    }

    // Calculate alternative IBAN, write warning if not the same
    /*
    form.alt_iban.value = extern(CalcAltIBAN(country, bank, account));
    if (form.alt_iban.value != form.iban.value)
      form.alt_iban.value += " (*)";
    else
      form.alt_iban.value = "";
   */
  }
}

// Check the syntax and the checksum of the IBAN.
function checkIBAN()
{
  var form = document.forms[0];
  var iban = intern(form.f_cmp_iban.value);

  var code     = iban.substring(0, 2);
  var checksum = iban.substring(2, 4);
  var bban     = iban.substring(4);
  var country  = CountryData(code);

  // var codeInForm = form.f_ad_country.options[form.f_ad_country.selectedIndex].value;

  var err = null;
  if (country == null)
    err = _("Unbekannter Ländercode: ") + code;
  else if (InvalidIBANlength(country, iban))
    err = _("Die Länge der IBAN ") + iban.length + _(" ist nicht korrekt für ") +
          country.name + " (" + country.total_lng + ")";
  else
  {
    var bank_lng = country.bank_lng;
    var bank     = bban.substring(0, bank_lng);
    var account  = bban.substring(bank_lng);

    if (InvalidBank(country, bank))
      err = _("Die Bankleitzahl ") + bank + _(" ist nicht korrekt für ") +
            country.name;
    else if (InvalidAccount(country, account))
      err = _("Die Kontonummer ") + account + _(" ist nicht korrekt für ") +
            country.name;
    else if (!IBANokay(iban))
      err = _("Die Checksumme ser IBAN ist nicht korrekt!");
  }

  if (err)
  {
    form.f_cmp_iban.focus();
    alert(err);
    form.f_cmp_iban.value = "";
  }
  else
  {
    // Set okay image on BBAN side
    document.bban_img.src = okay_img.src;
    document.iban_img.src = blank_img.src;

    // Write results in form fields
    form.iban.value = extern(iban);
    for (var i = form.country.options.length - 1; i > 0; --i)
      if (form.country.options[i].value == code)
        break;
    form.country.selectedIndex = i;
    form.bank.value = bank;
    form.account.value = account;

    // Calculate alternative IBAN, write warning if not the same
    form.alt_iban.value = extern(CalcAltIBAN(country, bank, account));
    if (form.alt_iban.value != form.iban.value)
      form.alt_iban.value += " (*)";
    else
      form.alt_iban.value = "";
  }
}

// Write the selection bar into the form.
function WriteCountrySelectionBar()
{
  document.write('<select name="country" size="1">');
  document.write('<option value="??">&nbsp;</option>');
  var tab = CountryIndexTable();
  for (var i = 0; i < tab.length; ++i)
  {
    var country = iban_data[tab[i]];
    document.write('<option value="' + country.code + '">' +
                   country.name + ' (' + country.code + ')</option>');
  }
  document.write('</select>');
}

// Write a table with the country specific iban format.
function WriteCountryFormatTable()
{
  document.write('<table bgcolor="#99FFCC" width="100%" border="4">' +
                 ' <tr>' +
                 '  <th rowspan="2">&nbsp;</th>' +
                 '  <th rowspan="2">' + _("Country") + '<BR />Code</th>' +
                 '  <th colspan="3">' + _("Bank/Branch Code") + '</th>' +
                 '  <th colspan="3">' + _("Account Number") + '</th>' +
                 ' </tr>' +
                 ' <tr>' +
                 '  <th>' + _("check1") + '</th><th>' + _("bank") + '</th>' +
                 '  <th>' + _("branch") + '</th><th>' + _("check2") + '</th>' +
                 '  <th>' + _("number") + '</th><th>' + _("check3") + '</th>' +
                 ' </tr>');
  var tab = CountryIndexTable();
  for (var i = 0; i < tab.length; ++i)
  {
    var country = iban_data[tab[i]];
    document.write(' <tr>' +
                   '  <td>' + country.name + '</td>' +
                   '  <td align="center">' + country.code + '</td>');
    for (var f = 0; f < country.bank.length; ++f)
    {
      var lng = country.bank[f][0], typ = country.bank[f][1];
      if (lng > 0)
        document.write('  <td align="center">' + lng + ' ' + typ + '</td>');
      else
        document.write('  <td align="center">-</td>');
    }
    for (var f = 0; f < country.acc.length; ++f)
    {
      var lng = country.acc[f][0], typ = country.acc[f][1];
      if (lng > 0)
        document.write('  <td align="center">' + lng + ' ' + typ + '</td>');
      else
        document.write('  <td align="center">-</td>');
    }
  }
  document.write(' <tr>' +
                 '  <td colspan="2">&nbsp;</td>' +
                 '  <td colspan="8" align="center">' +
                    _("a = A-Z, n = 0-9, c = A-Z/a-z/0-9") +
                 '  </td>' +
                 ' </tr>' +
                 '</table>');
}

function _(s)
{
  var t = trans_tab[s];
  if (t)
    s = t;
  return s;
}

// Fill the translation table
function fill_trans_tab(trans_data)
{
  for (var i = 0; i < trans_data.length / 2; ++i)
    trans_tab[trans_data[2 * i]] = trans_data[2 * i + 1];

  // Translate the country names in the iban_data list
  for (var i = 0; i < iban_data.length; ++i)
    iban_data[i].name = _(iban_data[i].name);
}