/**
 *  Array that contains all country codes.
 */
var myCountryInfo = new Array(
    "AF", "93", "00", "0",
    "AL", "355", "00", "0",
    "DZ", "213", "00", "7",
    "AM", "374", "00", "8",
    "AU", "61", "0011", "0",
    "AT", "43", "00", "0",
    "AZ", "994", "810", "8",
    "BH", "973", "00", "",
    "BE", "32", "00", "0",
    "BG", "359", "00", "0",
    "CA", "1", "011", "",
    "CN", "86", "00", "0",
    "HR", "385", "00", "0",
    "CY", "357", "00", "0",
    "CZ", "420", "00", "",
    "DK", "45", "00", "",
    "EG", "20", "00", "0",
    "EE", "372", "00", "0",
    "FI", "358", "00", "0",
    "FR", "33", "00", "0",
    "DE", "49", "00", "0",
    "GH", "233", "00", "",
    "GR", "30", "00", "",
    "HK", "852", "001", "",
    "HU", "36", "00", "06",
    "IS", "354", "00", "0",
    "IN", "91", "00", "0",
    "ID", "62", "001", "0",
    "IR", "98", "00", "0",
    "IQ", "964", "00", "0",
    "IE", "353", "00", "0",
    "IL", "972", "00", "0",
    "IT", "39", "00", "",
    "JO", "962", "00", "0",
    "KW", "965", "00", "0",
    "LB", "961", "00", "0",
    "LI", "423", "00", "",
    "LU", "352", "00", "",
    "MT", "356", "00", "",
    "MA", "212", "00", "0",
    "MZ", "258", "00", "0",
    "NL", "31", "00", "0",
    "NZ", "64", "00", "0",
    "NO", "47", "00", "",
    "OM", "968", "00", "0",
    "PK", "92", "00", "0",
    "PL", "48", "00", "0",
    "PT", "351", "00", "",
    "QA", "974", "00", "0",
    "RO", "40", "00", "0",
    "RU", "7", "810", "0",
    "SA", "966", "00", "0",
    "SG", "65", "001", "",
    "SI", "386", "00", "0",
    "ZA", "27", "09", "0",
    "ES", "34", "00", "",
    "SD", "249", "00", "0",
    "SE", "46", "00", "0",
    "CH", "41", "00", "0",
    "SY", "963", "00", "0",
    "TH", "66", "001", "0",
    "TN", "216", "00", "0",
    "TR", "90", "00", "0",
    "AE", "971", "00", "0",
    "UK", "44", "00", "0",
    "US", "1", "011", "",
    "YE", "967", "00", "0",
    "YU", "381", "99", "0"
);

/**
 *  Usage:
 *    To populate a combo box with the supported countries, use the following functions:
 *      countCountries() - Iterate this many times in a loop.
 *      getCountryOnIndex() - To get the name of a country in the loop.
 *    To get the country handle of the selected option, use the following function:
 *      getCountryOnName()
 *    To calcuate the MSISDN, use the following function:
 *      computeMsisdn()
 */

/**
 *  Error codes from computerMsisdn().
 */
var COMPUTEERROR_OK = 0;
var COMPUTEERROR_TOOSHORT = 1;    // The MSISDN is less than 8 digits long
var COMPUTEERROR_TOOLONG = 2;     // The MSISDN is more than 15 digits long
var COMPUTEERROR_INVALID = 3;     // The MSISDN is given without +, IDD or NDD (e.g. 4670xxx instead of +4670xxx or 070xxx)
var COMPUTEERROR_INVALIDAREA = 4; // The number does not belong to a mobile phone area (e.g. 08xxx)

var myComputeError = 0;


/**
 *  Helper function to extract info from the myCountryInfo array. Returns the index of a country in the array.
 *  @param strCountry The country handle to get the index of.
 *  @return the index of the country in the array, or -1 if the country is not supported.
 */
function getCountryInfoIndex( strCountry )
{
    var iIndex;

    for( iIndex = 0; iIndex < myCountryInfo.length; iIndex += 4 )
    {
        if( myCountryInfo[ iIndex ] == strCountry )
        {
            return iIndex;
        }
    }

    // Country not found
    return -1;
}

/**
 *  Helper function to extract info from the myCountryNames array. Returns the index of a country in the array.
 *  @param strCountry The country handle to get the index of.
 *  @return the index of the country in the array, or -1 if the country is not supported.
 */
function getCountryNamesIndex( strCountry )
{
    var iIndex;

    for( iIndex = 0; iIndex < myCountryNames.length; iIndex += 2 )
    {
        if( myCountryNames[ iIndex ] == strCountry )
        {
            return iIndex;
        }
    }

    // Country not found
    return -1;
}

/**
 *  Obtains the Country Code of a country.
 *  @param strCountry The country handle to get the country code of.
 *  @return the Country Code of the country, or an empty string if the country is not supported.
 */
function getCode( strCountry )
{
    var iCountryIndex = getCountryInfoIndex( strCountry );

    if( iCountryIndex >= 0 )
    {
        return myCountryInfo[ iCountryIndex + 1 ];
    }

    return "";
}

/**
 *  Obtains the International Dialing Direct prefix of a country.
 *  @param strCountry The country handle to get the IDD of.
 *  @return the IDD of the country, or an empty string if the country is not supported.
 */
function getIdd( strCountry )
{
    var iCountryIndex = getCountryInfoIndex( strCountry );

    if( iCountryIndex >= 0 )
    {
        return myCountryInfo[ iCountryIndex + 2 ];
    }

    return "";
}

/**
 *  Obtains the National Dialing Direct prefix of a country.
 *  @param strCountry The country handle to get the NDD of.
 *  @return the NDD of the country, or an empty string if the country is not supported.
 */
function getNdd( strCountry )
{
    var iCountryIndex = getCountryInfoIndex( strCountry );

    if( iCountryIndex >= 0 )
    {
        return myCountryInfo[ iCountryIndex + 3 ];
    }

    return "";
}

/**
 *  Obtains the mobile phone Area code prefix of a country.
 *  @param strCountry The country handle to get the mobile area code of.
 *  @return the mobile phone Area code of the country, or an empty string if the country is not supported.
 */
function getArea( strCountry )
{
    // Area not supported by the myCountryInfo array.
    return "";
}

/**
 *  Obtains the Name of a country.
 *  @param strCountry The country handle to get the name of.
 *  @return the Name of the country, or an empty string if the country is not supported.
 */
function getName( strCountry )
{
    var iCountryIndex = getCountryNamesIndex( strCountry );

    if( iCountryIndex >= 0 )
    {
        return myCountryNames[ iCountryIndex + 1 ];
    }

    return "";
}

/**
 *  Obtain the number of supported countries.
 *  @return the number of countries that are supported.
 */
function countCountries()
{
    return myCountryNames.length / 2;
}

/**
 *  Obtain the handle of the country with the specified index. Used to iterate through the countries.
 *  @param iIndex the index of the country to get.
 *  @return the country handle with the given index.
 */
function getCountryOnIndex( iIndex )
{
    return myCountryNames[ iIndex * 2 ];
}

/**
 *  Obtain the handle of the country with the specified name.
 *  @param strName the localized name of the country.
 *  @return the country handle with the given name.
 */
function getCountryOnName( strName )
{
    var iIndex;

    for( iIndex = 0; iIndex < myCountryNames.length; iIndex += 2 )
    {
        if( myCountryNames[ iIndex + 1 ] == strName )
        {
            return myCountryNames[ iIndex ];
        }
    }

    // Country not found
    return "";
}

/**
 *  Find the country that has the specified country code.
 *  @param Number starting with the country code of the sought for country.
 *  @return Country handle of the country, or an empty string if not supported.
 */
function getCountryOnCode( strNumber )
{
    var iIndex;

    for( iIndex = 0; iIndex < myCountryInfo.length; iIndex += 4 )
    {
        var strCode = myCountryInfo[ iIndex + 1 ];
        if( strNumber.substr( 0, strCode.length ) == strCode )
        {
            return myCountryInfo[ iIndex ];
        }
    }

    // Country not found
    return "";
}

/**
 *  Removes all non-digits from the specified string. The only non-digit character that is allowed is a + in
 *  the first position.
 *  @param strOriginal String to extract digits from.
 *  @return A string with only digits, except for an optional + in the first position.
 */
function extractDigits( strOriginal )
{
    var strResult = "";
    var iIndex;

    for( iIndex = 0; iIndex < strOriginal.length; ++iIndex )
    {
        var chChar = strOriginal.charAt( iIndex );
        if( chChar == "+" )
        {
            // Only keep the + if it is in the first position
            if( strResult.length == 0 )
            {
                strResult += chChar;
            }
        }
        else if( chChar >= '0' && chChar <= '9' )
        {
            strResult += chChar;
        }
        else
        {
            // Do nothing. This will remove the invalid char.
        }
    }

    return strResult;
}

/**
 *  Returns error that occurred during the last computation of an MSISDN. See codes in the top of this file.
 *  @return error from computerMsisdn().
 */
function getComputeError()
{
    return myComputeError;
}

/**
 *  Converts a phone number to MSISDN format, and returns it. If the number cannot be converted to an MSISDN,
 *  the function returns an empty string. A reason for not being able to convert it can be retreived by calling
 *  the getComputerError() function.
 *  @param strNumber The number to convert.
 *  @param strCountry Handle of the country in which the number is assumed to originate. May be empty.
 *  @return The number on an MSISDN format, or an empty string if it could not be converted.
 */
function computeMsisdn( strNumber, strCountry )
{
    // 1. Remove all non-digits:
    var strWorkNumber = extractDigits( strNumber );
    if( strWorkNumber.length == 0 )
    {
        myComputeError = COMPUTEERROR_TOOSHORT;
        return "";
    }

    var bParseIdd = false;
    var bParseNdd = false;
    var bParseArea = false;
    var iPos = 0;
    var strMsisdn = "+";

    // 2. Check if the number starts with an IDD or +
    if( strWorkNumber.charAt( iPos ) == '+' )
    {
        // The number starts with a + (example: +46 73...)
        bParseIdd = true;
        ++iPos;
    }
    else if( strCountry.length != 0 )
    {
        var strIdd = getIdd( strCountry );
        var strNdd = getNdd( strCountry );
        if( strIdd.length != 0 && strWorkNumber.substr( iPos, strIdd.length ) == strIdd )
        {
            // The number starts with the IDD (example: 0046 73...)
            bParseIdd = true;
            iPos += strIdd.length;
        }
        else if( strNdd.length == 0 || strWorkNumber.substr( iPos, strNdd.length ) == strNdd )
        {
            // The number starts with the NDD (example: 073...)
            bParseIdd = false;
            bParseNdd = false;
            bParseArea = true;
            strMsisdn += getCode( strCountry );
            iPos += strNdd.length;
        }
        else
        {
            // The number doesn't start with the IDD or the NDD. Here we could accept it and assume it starts with a country code.
            // If so, uncomment the line below.
            // bParseIdd = true;
            // A better solution is to ask the user to specify it on the correct format, so we return an error.
            // @@@ ToDo Set Error
            myComputeError = COMPUTEERROR_INVALID;
            return "";
        }
    }
    else
    {
        // Number given without a +, but with no country code specified. We can't parse it!
        myComputeError = COMPUTEERROR_INVALID;
        return "";
    }

    // 3. Parse the IDD
    if( bParseIdd )
    {
        // Try to find a country on the country code
        strCountry = getCountryOnCode( strWorkNumber.substr( iPos ) );
        if( strCountry.length == 0 )
        {
            // No matching country found which either means that the given number is not supported
            // or that it is invalid.
            // We guess it is valid for now though... can't parse NDD or Area though.
            bParseNdd = false;
            bParseArea = false;
        }
        else
        {
            bParseNdd = true;
            var strCode = getCode( strCountry );
            strMsisdn += strCode;
            iPos += strCode.length;
        }
    }

    // 4. Parse the NDD
    if( bParseNdd )
    {
        // Here we are sure that strCountry is valid
        var strNdd = getNdd( strCountry );
        if( strNdd.length == 0 || strWorkNumber.substr( iPos, strNdd.length ) == strNdd )
        {
            // The NDD should not be in a MSISDN, so we skip it
            iPos += strNdd.length;
            bParseArea = true;
        }
        else
        {
            bParseArea = false;
        }
    }

    // 5. Validate that the area code is a valid mobile phone area code
    //    This is not supported
/*
    if( bParseArea )
    {
        var strArea = getArea( strCountry );
        if( strArea.length == 0 || strWorkNumber.substr( iPos, strArea.length ) == strArea )
        {
            // The given area is not a mobile phone area -- invalidate number
            myComputeError = COMPUTEERROR_INVALIDAREA;
            return "";
        }
    }
*/

    // Copy the rest of the number -- make sure the size of it is [8,15] (excluding the '+' sign)
    strMsisdn += strWorkNumber.substr( iPos );

    var iLowerLimit = 8;
    var iUpperLimit = 15;
    if( strMsisdn.length < iLowerLimit + 1 )
    {
        // MSISDN is too short
        myComputeError = COMPUTEERROR_TOOSHORT;
        return "";
    }
    if( strMsisdn.length > iUpperLimit + 1 )
    {
        // MSISDN is too long
        myComputeError = COMPUTEERROR_TOOLONG;
        return "";
    }

    myComputeError = COMPUTEERROR_OK;
    return strMsisdn;
}
