var PHONE_NATIONAL_NUMBER_LENGTH          = 10;
var PHONE_MIN_INTERNATIONAL_NUMBER_LENGTH = 8;

/******************************************************************************
  PHONE_PhoneNumber c'tor

    Attempts to extract a phone number from a string. Allows and preserves
    formatting characters only in the national number.


 ******************************************************************************/
function PHONE_PhoneNumber( input )
{
    try
    {
        this.m_hasCCDelimiter    = false;
        this.m_countryCode       = CMD_COUNTRY_CODE_CONTROL_ID;
        this.m_countryCodeOffset = 0;

        this.m_nationalNumber    = '';
        this.m_nationalNumberWithFormatting = '';

        if ( input === null || input === undefined )
        {
            return;
        }

        // remove all non-digits and non-+
        var scrubbedOriginalNumber = input.replace( /[^0-9+]/g, '' );

        if ( scrubbedOriginalNumber === '' )
        {
            return;
        }

        // parse international number taking special care to ignore CC delimiters
        var matches = scrubbedOriginalNumber.match( /^(00|011|[+]|0)?(.*)$/ );

        var prefix                   = ( matches[1] !== undefined && matches[1] !== null ? matches[1] : '' );
        var unScrubbedNationalNumber = ( matches[2] !== undefined && matches[2] !== null ? matches[2] : '' );

        // remove all non-digits
        var scrubbedNationalNumber = unScrubbedNationalNumber.replace( /[^0-9]/g, '' );

        if ( prefix === '0' )
        {
            // the rest of the number is a national number
            // we don't know the country code
            this.SetNationalNumber( prefix, scrubbedNationalNumber, input );

            return;
        }

        if ( prefix.length != 0 )
        {
            this.m_hasCCDelimiter = true;
        }
        // There is no prefix and the number has either 10 digits or 11 starting with 1.
        else if ( scrubbedNationalNumber.length == 10 ||
                ( scrubbedNationalNumber.length == 11 && scrubbedNationalNumber.charAt( 0 ) === CMD_COUNTRY_CODE_NANP ) )
        {
            // presume this is a US number
            this.m_countryCode = CMD_COUNTRY_CODE_NANP;

            if ( scrubbedNationalNumber.length == 11 )
            {
                this.SetNationalNumber( '', scrubbedNationalNumber.substr( CMD_COUNTRY_CODE_NANP.length ), input );
            }
            else
            {
                this.m_nationalNumber               = scrubbedNationalNumber;
                this.m_nationalNumberWithFormatting = input;
            }

            return;
        }

        this.m_countryCodeOffset = prefix.length;

        // lookup the country code
        for ( var ccLength = 3; ccLength > 0; --ccLength )
        {
            for ( var countryCodeIndex = 0; countryCodeIndex < CMD_COUNTRY_CODES.length; ++countryCodeIndex )
            {
                if ( CMD_COUNTRY_CODES[ countryCodeIndex ].m_countryCode.length == ccLength &&
                     CMD_COUNTRY_CODES[ countryCodeIndex ].m_countryCode == scrubbedNationalNumber.substr( 0, ccLength ) )
                {
                    this.m_countryCode = CMD_COUNTRY_CODES[ countryCodeIndex ].m_countryCode;

                    this.SetNationalNumber( prefix, scrubbedNationalNumber.substr( ccLength ), input );

                    return;
                }
            }
        }

        // don't know the CC
        this.SetNationalNumber( prefix, scrubbedNationalNumber, input );
    }
    catch ( e )
    {
        EX_ASSERT_NO_EXCEPTIONS( e, 'PHONE_PhoneNumber::PHONE_PhoneNumber( ) : number="' + input + '"' );
    }
}
PHONE_PhoneNumber.prototype =
{
    HasCCDelimiter : function( )
    {
        return this.m_hasCCDelimiter;
    },

    GetNationalNumber : function( )
    {
        return this.m_nationalNumber;
    },

    GetNationalNumberWithFormatting : function( )
    {
        return this.m_nationalNumberWithFormatting;
    },

    GetCountryCode : function( )
    {
        return this.m_countryCode;
    },

    GetFullyQualifiedString : function( )
    {
        try
        {
            if ( this.m_countryCode === CMD_COUNTRY_CODE_CONTROL_ID )
            {
                throw new EX_ProgrammingError( "Country code invalid" );
            }

            var normalizedNumber = '+' + this.m_countryCode;

            if ( this.m_nationalNumberWithFormatting.charAt( 0 ) !== ' ' )
            {
                normalizedNumber += ' ';
            }

            normalizedNumber += this.m_nationalNumberWithFormatting;

            return normalizedNumber;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, 'PHONE_PhoneNumber::GetFullyQualifiedString( )' );
        }
    },

    GetStateString : function( )
    {
        return  "" +
                "m_hasCCDelimiter               = "  + this.m_hasCCDelimiter                + "'\n" +
                "m_countryCode                  = '" + this.m_countryCode                   + "'\n" +
                "m_countryCodeOffset            = '" + this.m_countryCodeOffset             + "'\n" +
                "m_nationalNumber               = '" + this.m_nationalNumber                + "'\n" +
                "m_nationalNumberWithFormatting = '" + this.m_nationalNumberWithFormatting  + "'\n" +
                "m_nationalNumberOffset         = '" + this.m_nationalNumberOffset          + "'";
    },

    UpdateCountryCode : function( countryCode )
    {
        try
        {
            if ( countryCode === CMD_COUNTRY_CODE_CONTROL_ID )
            {
                throw new EX_ProgrammingError( "Invalid country code: " + countryCode );
            }

            if ( this.m_countryCode === countryCode )
            {
                return;
            }

            // Country codes are different. Only allow setting when the CC is 'empty'.
            if ( this.m_countryCode !== CMD_COUNTRY_CODE_CONTROL_ID )
            {
                throw new EX_ProgrammingError( "Country code is already set." );
            }

            this.SetCountryCode( countryCode );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, 'PHONE_PhoneNumber::UpdateCountryCode( )' );
        }
    },

    OverrideCountryCodeInferrence : function( countryCode )
    {
        try
        {
            if ( this.m_hasCCDelimiter )
            {
                throw new EX_ProgrammingError( "Country code is explicitly delimited" );
            }

            this.m_nationalNumber = this.m_countryCode + this.m_nationalNumberWithFormatting;
            this.m_countryCode = CMD_COUNTRY_CODE_CONTROL_ID;
            this.m_countryCodeOffset = 0;

            this.SetCountryCode( countryCode );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, 'PHONE_PhoneNumber::OverrideCountryCodeInferrence( )' );
        }
    },

    SetCountryCode : function( countryCode )
    {
        try
        {
            if ( countryCode === CMD_COUNTRY_CODE_CONTROL_ID )
            {
                throw new EX_ProgrammingError( "Invalid country code: " + countryCode );
            }

            this.m_countryCode = countryCode;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, 'PHONE_PhoneNumber::SetCountryCode( )' );
        }
    },

    SetNationalNumber : function( countryCodePrefix, scrubbedNationalNumber, input )
    {
        try
        {
            // Helper method used below
            function FindEndOfSubStringIndex( containedString, containingString )
            {
                var containedIndex = 0;
                for ( var index = 0; index < containingString.length; ++index )
                {
                    if ( containingString.charAt( index ) !== containedString.charAt( containedIndex ) )
                    {
                        continue;
                    }

                    if ( ++containedIndex == containedString.length )
                    {
                        return index + 1;
                    }
                }

                throw new EX_ProgrammingError( "Sub string '" + containedString + "' not in containing string '" + containingString + "'" );
            }

            this.m_nationalNumber = scrubbedNationalNumber;

            var nationalNumberIndex = 0;

            if ( countryCodePrefix.length != 0 )
            {
                nationalNumberIndex += FindEndOfSubStringIndex( countryCodePrefix, input );
            }

            if ( this.m_countryCode !== CMD_COUNTRY_CODE_CONTROL_ID )
            {
                var startOfCountryCodeSubString = input.substr( nationalNumberIndex );

                nationalNumberIndex += FindEndOfSubStringIndex( this.m_countryCode, startOfCountryCodeSubString );
            }

            this.m_nationalNumberWithFormatting = input.substr( nationalNumberIndex );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "PHONE_PhoneNumber::SetNationalNumber( )" );
        }
    }
};
