// This code is a part of the Telephony Framework C++ Library.
// Copyright (C) 1997 Chris Sells. All rights reserved.
// tPhoneNo.cpp: CtPhoneNo class implementation

#include "stdafx.h"
#include "tPhoneNo.h"
#include <ctype.h>  // isalpha(), toupper(), isdigit()
#include <tapi.h>   // tapiGetLocationInfo()

#define MAX_AC_SIZE 8
#define MAX_CC_SIZE 8
#define SPECIAL_NO_MAX  5

#ifndef TFAILED
#define TFAILED(t) (t < 0)
#endif

static inline
void Reset(char*& ac)
{
    delete [] ac; ac = 0;
}

static inline
char* StringDuplicate(LPCSTR szIn)
{
    const int   nSize = (szIn ? ::strlen(szIn) + 1 : 0);
    char*       szOut = 0;
    if( nSize )
    {
        szOut = new char[nSize];
        ::strcpy(szOut, szIn);
    }
    return szOut;
}

CtPhoneNo::CtPhoneNo()
:
    m_pszPhoneNo(0),
    m_pszAreaCode(0),
    m_pszCountryCode(0),
    m_pszCanonical(0),
    m_pszDisplayable(0),
    m_pszTranslatable(0)
{
    CheckDefaults();
}

CtPhoneNo::CtPhoneNo(LPCSTR szWholePhoneNo)
:
    m_pszPhoneNo(0),
    m_pszAreaCode(0),
    m_pszCountryCode(0),
    m_pszCanonical(0),
    m_pszDisplayable(0),
    m_pszTranslatable(0)
{
    SetWholePhoneNo(szWholePhoneNo);
}

CtPhoneNo::CtPhoneNo(LPCSTR szCountryCode, LPCSTR szAreaCode, LPCSTR szPhoneNo)
:
    m_pszCountryCode(::StringDuplicate(szCountryCode)),
    m_pszAreaCode(::StringDuplicate(szAreaCode)),
    m_pszPhoneNo(::StringDuplicate(szPhoneNo)),
    m_pszCanonical(0),
    m_pszDisplayable(0),
    m_pszTranslatable(0)
{
}

CtPhoneNo::CtPhoneNo(DWORD nCountryCode, LPCSTR szAreaCode, LPCSTR szPhoneNo)
:
    m_pszCountryCode(0),
    m_pszAreaCode(::StringDuplicate(szAreaCode)),
    m_pszPhoneNo(::StringDuplicate(szPhoneNo)),
    m_pszCanonical(0),
    m_pszDisplayable(0),
    m_pszTranslatable(0)
{
    SetCountryCode(nCountryCode);
}

CtPhoneNo::CtPhoneNo(const CtPhoneNo& pno)
:
    m_pszPhoneNo(0),
    m_pszAreaCode(0),
    m_pszCountryCode(0),
    m_pszCanonical(0),
    m_pszDisplayable(0),
    m_pszTranslatable(0)
{
    Copy(pno);
}

CtPhoneNo& CtPhoneNo::operator=(const CtPhoneNo& pno)
{
    if( this != &pno )
    {
        ResetAll();
        Copy(pno);
    }
    return *this;
}

CtPhoneNo::~CtPhoneNo()
{
    ResetAll();
}

void CtPhoneNo::ResetAll()
{
    ::Reset(m_pszCountryCode);
    ::Reset(m_pszAreaCode);
    ::Reset(m_pszPhoneNo);
    ::Reset(m_pszCanonical);
    ::Reset(m_pszDisplayable);
    ::Reset(m_pszTranslatable);
}

LPCSTR CtPhoneNo::GetCountryCode()
{
    return m_pszCountryCode;
}

DWORD CtPhoneNo::GetCountryCodeNum()
{
    return (m_pszCountryCode ? ::atoi(m_pszCountryCode) : 0);
}

LPCSTR CtPhoneNo::GetAreaCode()
{
    return m_pszAreaCode;
}

LPCSTR CtPhoneNo::GetPhoneNo()
{
    return m_pszPhoneNo;
}

// Format: "+CC [(AC) ]PN"
LPCSTR CtPhoneNo::GetCanonical()
{
    if( !m_pszCanonical &&
        m_pszCountryCode &&
        m_pszPhoneNo )
    {
        int nLen = 1 + // "+"
                   (::strlen(m_pszCountryCode) + 1) + // "CC "
                   (m_pszAreaCode ? ::strlen(m_pszAreaCode) + 3 : 0) +  // "[(AC) ]"
                   ::strlen(m_pszPhoneNo);  // "PN"

        if( m_pszCanonical = new char[nLen + 1] )
        {
            if( m_pszAreaCode )
            {
                ::wsprintf(m_pszCanonical, "+%s (%s) %s", m_pszCountryCode,
                           m_pszAreaCode, m_pszPhoneNo);
            }
            else
            {
                ::wsprintf(m_pszCanonical, "+%s %s", m_pszCountryCode,
                           m_pszPhoneNo);
            }
        }
    }

    return m_pszCanonical;
}

// Format: "[CC ][(AC) ][PN]"
LPCSTR CtPhoneNo::GetDisplayable()
{
    if( !m_pszDisplayable )
    {
        // Padding depends on data present
        int nLen = (m_pszCountryCode ? ::strlen(m_pszCountryCode) + 1 : 0) +
                   (m_pszAreaCode ? ::strlen(m_pszAreaCode) + 3 : 0) +
                   (m_pszPhoneNo ? ::strlen(m_pszPhoneNo) : 0);

        if( nLen && (m_pszDisplayable = new char[nLen + 1]) )
        {
            int nOffset = 0;

            if( m_pszCountryCode )
            {
                nOffset += ::wsprintf(m_pszDisplayable + nOffset,
                                      "%s ", m_pszCountryCode);
            }

            if( m_pszAreaCode )
            {
                nOffset += ::wsprintf(m_pszDisplayable + nOffset,
                                      "(%s) ", m_pszAreaCode);
            }

            if( m_pszPhoneNo )
            {
                nOffset += ::wsprintf(m_pszDisplayable + nOffset,
                                      "%s", m_pszPhoneNo);
            }
        }
    }
    return m_pszDisplayable;
}

LPCSTR CtPhoneNo::GetTranslatable(LPCSTR pszMap)
{
    if( !m_pszTranslatable )
    {
        LPCSTR  sz = 0;
        if( ((sz = GetCanonical()) || (sz = GetDisplayable())) &&
             (m_pszTranslatable = new char[::strlen(sz) + 1]) )
        {
            // Convert letters to digits (if we're supposed to)
            if( pszMap )
            {
                ASSERT(::strlen(pszMap) >= 26);

                int n = 0;
                while( sz[n] )
                {
                    m_pszTranslatable[n] = (isalpha(sz[n])
                                            ? pszMap[toupper(sz[n]) - 'A']
                                            : sz[n]);
                    n++;
                }
                m_pszTranslatable[n] = 0;
            }
            else
            {
                ::strcpy(m_pszTranslatable, sz);
            }
        }
    }

    return m_pszTranslatable;
}

void CtPhoneNo::SetWholePhoneNo(LPCSTR szWholePhoneNo)
{
    ResetAll();

    // Try canonical format
    if( szWholePhoneNo && *szWholePhoneNo == '+' )
    {
        SetCanonical(szWholePhoneNo);
    }
    // Try our best...
    else if( szWholePhoneNo && *szWholePhoneNo )
    {
        /// Count digits and get pointers to first N digits
        int         nDigits = 0;
        const int   nFirstN = 5;
        const char* apcDigitN[nFirstN] = {0, 0, 0, 0, 0};

        LPCSTR  pcDigit = szWholePhoneNo;
        while( *pcDigit )
        {
            if( isalnum(*pcDigit) )
            {
                if( ++nDigits <= nFirstN )
                {
                    apcDigitN[nDigits-1] = pcDigit;
                }
            }
            pcDigit++;
        }

        int nDigitsUsed = 0;

        // Assume 5 or less digits is an extension or special number, e.g. 911
        if( nDigits > SPECIAL_NO_MAX )
        {
            // If it's not a special number, fill in default area code and country code
            CheckDefaults();
        }

        // If the number is US, set the country code
        // and reset the beginning of the number to start at the 2nd digit
        if( nDigits == 11 && apcDigitN[0] && *(apcDigitN[0]) == '1' )
        {
            delete [] m_pszCountryCode;
            m_pszCountryCode = ::StringDuplicate("1");
            nDigitsUsed++;
        }

        // If the number of digits is 10 and we're in the US, pull out area code
        if( (nDigits - nDigitsUsed) == 10 &&
            m_pszCountryCode && m_pszCountryCode[0] == '1' && m_pszCountryCode[1] == 0 )
        {
            delete [] m_pszAreaCode;
            if( m_pszAreaCode = new char[4] )
            {
                m_pszAreaCode[0] = *(apcDigitN[0 + nDigitsUsed]);
                m_pszAreaCode[1] = *(apcDigitN[1 + nDigitsUsed]);
                m_pszAreaCode[2] = *(apcDigitN[2 + nDigitsUsed]);
                m_pszAreaCode[3] = 0;
                nDigitsUsed += 3;
            }
        }

        // The phone number is the rest
        delete [] m_pszPhoneNo;
        m_pszPhoneNo = ::StringDuplicate(apcDigitN[nDigitsUsed]);
    }
}

// Format: "+CC [(AC) ]PN"
void CtPhoneNo::SetCanonical(LPCSTR szCanonical)
{
    ASSERT(szCanonical && szCanonical[0] == '+' && isdigit(szCanonical[1]));

    ClearConstructs();

    int         nCount;
    const char* pcFirst;
    const char* pcDigit = szCanonical + 1;

    // Get country code (pcDigit will be one past end of CC)
    pcFirst = pcDigit;
    while( isdigit(*pcDigit) ) pcDigit++;
    nCount = pcDigit - pcFirst;
    ASSERT(nCount > 0);
    if( m_pszCountryCode = new char[nCount + 1] )
    {
        ::strncpy(m_pszCountryCode, pcFirst, nCount);
        m_pszCountryCode[nCount] = 0;    
    }

    // Skip the space
    ASSERT(*pcDigit == ' ');
    pcDigit++;

    // Check for area code
    if( *pcDigit == '(' )
    {
        // Skip right paren
        pcDigit++;
        ASSERT(isdigit(*pcDigit));

        // Get area code (pcDigit will be one past end of AC)
        pcFirst = pcDigit;
        while( isdigit(*pcDigit) ) pcDigit++;
        nCount = pcDigit - pcFirst;
        ASSERT(nCount > 0);
        if( m_pszAreaCode = new char[nCount + 1] )
        {
            ::strncpy(m_pszAreaCode, pcFirst, nCount);
            m_pszAreaCode[nCount] = 0;    
        }

        // Skip the right paren and the space
        ASSERT(*pcDigit == ')');
        ASSERT(*(pcDigit + 1) == ' ');
        pcDigit += 2;
    }

    // Get phone no. (everything else)
    ASSERT(isalnum(*pcDigit));
    pcFirst = pcDigit;
    while( *pcDigit ) pcDigit++;
    nCount = pcDigit - pcFirst;
    ASSERT(nCount > 0);
    if( m_pszPhoneNo = new char[nCount + 1] )
    {
        ::strncpy(m_pszPhoneNo, pcFirst, nCount);
        m_pszPhoneNo[nCount] = 0;    
    }
}

void CtPhoneNo::SetCanonical(LPCSTR szCountryCode, LPCSTR szAreaCode, LPCSTR szPhoneNo)
{
    ResetAll();

    ASSERT(szCountryCode && *szCountryCode);
    m_pszCountryCode = ::StringDuplicate(szCountryCode);

    if( szAreaCode && *szAreaCode )
    {
        m_pszAreaCode = ::StringDuplicate(szAreaCode);
    }

    ASSERT(szPhoneNo && *szPhoneNo);
    m_pszPhoneNo = ::StringDuplicate(szPhoneNo);
}

void CtPhoneNo::SetCanonical(DWORD nCountryCode, LPCSTR szAreaCode, LPCSTR szPhoneNo)
{
    char    sz[MAX_CC_SIZE];
    SetCanonical(::itoa(nCountryCode, sz, 10), szAreaCode, szPhoneNo);
}

void CtPhoneNo::ResetToLocation()
{
    ResetAll();
    CheckDefaults();
}

void CtPhoneNo::SetCountryCode(LPCSTR szCountryCode)
{
    ClearConstructs();
    ::Reset(m_pszCountryCode);

    if( szCountryCode && *szCountryCode )
    {
        m_pszCountryCode = ::StringDuplicate(szCountryCode);
    }
}

void CtPhoneNo::SetCountryCode(DWORD nCountryCode)
{
    ClearConstructs();
    ::Reset(m_pszCountryCode);

    if( nCountryCode )
    {
        char    sz[MAX_CC_SIZE];
        m_pszCountryCode = ::StringDuplicate(::itoa(nCountryCode, sz, 10));
    }
}

void CtPhoneNo::SetAreaCode(LPCSTR szAreaCode)
{
    ClearConstructs();
    ::Reset(m_pszAreaCode);

    if( szAreaCode && *szAreaCode )
    {
        m_pszAreaCode = ::StringDuplicate(szAreaCode);
    }
}

void CtPhoneNo::SetPhoneNo(LPCSTR szPhoneNo)
{
    ClearConstructs();
    ::Reset(m_pszPhoneNo);

    if( szPhoneNo && *szPhoneNo )
    {
        m_pszPhoneNo = ::StringDuplicate(szPhoneNo);
    }
}

void CtPhoneNo::ClearConstructs()
{
    ::Reset(m_pszCanonical);
    ::Reset(m_pszDisplayable);
    ::Reset(m_pszTranslatable);
}

void CtPhoneNo::CheckDefaults()
{
    if( !m_pszAreaCode && !m_pszCountryCode )
    {
        if( !(m_pszAreaCode = new char[MAX_AC_SIZE]) ||
            !(m_pszCountryCode = new char[MAX_CC_SIZE]) ||
            TFAILED(::tapiGetLocationInfo(m_pszCountryCode, m_pszAreaCode)) )
        {
            ::Reset(m_pszCountryCode);
            ::Reset(m_pszAreaCode);
        }
    }
}

void CtPhoneNo::Copy(const CtPhoneNo& pno)
{
    m_pszCountryCode = ::StringDuplicate(pno.m_pszCountryCode);
    m_pszAreaCode = ::StringDuplicate(pno.m_pszAreaCode);
    m_pszPhoneNo = ::StringDuplicate(pno.m_pszPhoneNo);
}

