// calcexp.cpp : implementation file
//

#include "stdafx.h"
#include "resource.h"

#ifdef _CHEN_

#include "calcexp.h"
#include "ctype.h"


#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif


/////////////////////////////////////////////////////////////////////////////
// Global function prototypes.
void CalculateCmd(int nArgc, char* pszArgv[]);


/////////////////////////////////////////////////////////////////////////////
// Public functions.

// C expression calculator.
void CalculateCmd(int nArgc, char* pszArgv[])
{
    // Assertion of the input parameters.
    ASSERT( nArgc >= 1 );
    for ( int i = 0; i < nArgc; i++ ) {
        ASSERT ( pszArgv[i] );
    }

    // Execute C expression calculation.
    CExpression* pExp = new CExpression;
    if ( !pExp ) {
        AfxMessageBox("Insufficient Memory !");
        return;
    }
    else {
        pExp->ExpAction(nArgc, pszArgv);
        delete pExp;
    }
}


/////////////////////////////////////////////////////////////////////////////
// CExpression construction & destruction

CExpression::CExpression()
    : m_nErrorID(noError)
{
}

CExpression::~CExpression()
{
}

void CExpression::ExpAction(int nArgc, char* pszArgv[])
{
    // Assertion of the input parameters.
    ASSERT ( nArgc >= 0 );
    for ( int i = 0; i < nArgc; i++ ) {
        ASSERT ( pszArgv[i] );
    }
    
    // Parse the parameters.
    switch ( nArgc ) {
        case 1:
            break;
        default:
            break;
    }

    // Get the input.
    CString strInput = "";
    for ( i = 1; i < nArgc; i++ ) {
        strInput += pszArgv[i];
    }

    // Calculate the C expression.
    CString strOutput;
    CalcExp(strInput, strOutput);
    
    // Display the result.
    //AfxMessageBox(m_strOutput);
    ShowLine(m_strOutput.GetBuffer(m_strOutput.GetLength()));

}   // End of CHelp::HelpAction().


/////////////////////////////////////////////////////////////////////////////
// CExpression implementation

void CExpression::CalcExp(const CString strInput, CString& strOutput)
{
    m_strInput = strInput;
    SkipWhiteSpace(m_strInput);

    m_strOutput = "";
    
    m_pszProg = m_strInput.GetBuffer(m_strInput.GetLength());

    do {
        double dResult;
        GetExp(dResult);
                 
        char szResult[maxToken+1];
        sprintf(szResult, "%f", dResult);
        
        m_strOutput += szResult;
    } while ( *m_pszProg );
    
    strOutput = m_strOutput;
}

void CExpression::GetToken(void)
{
    if ( noError != m_nErrorID ) {
        return;
    }
        
    char* p;
    
    m_chTokenType = undefine;

    p = m_pszToken;
    
    char* pszDelimiter = "+-*/%^()";
    if ( IsDelimiter(*m_pszProg, pszDelimiter) ) {
        m_chTokenType = delimiter;
        *p++ = *m_pszProg++;
    }
    else if ( isdigit(*m_pszProg) || '.' == *m_pszProg || ',' == *m_pszProg ) {
        while ( *m_pszProg && !IsDelimiter(*m_pszProg, pszDelimiter) ) {
            *p++ = *m_pszProg++;
        }
        m_chTokenType = number;
    }
    else if ( *m_pszProg ) {
        m_chTokenType = variable;
        *p++ = *m_pszProg++;
    }

    *p = 0;
}

void CExpression::GetExp(double& dResult)
{
    GetToken();
    
    if ( !*m_pszToken ) {
        m_nErrorID = syntaxError;
        DisplayErrorMessage();
        return;
    }
    
    Level1(dResult);
}

void CExpression::Level1(double& dResult)
{
    if ( variable == m_chTokenType ) {
        // Cannot support Variable.
        return;
    }
    else {
        Level2(dResult);
    }
}

void CExpression::Level2(double& dResult)
{
    char op;
    
    Level3(dResult);
    
    while ( (op = *m_pszToken) == '+' || op == '-' ) {
        GetToken();
        double dHold;
        Level3(dHold);
        Arith(op, dResult, dHold);
    }
}

void CExpression::Level3(double& dResult)
{
    char op;
    
    Level4(dResult);
    
    while ( (op = *m_pszToken) == '*' || op == '/' || op == '%' ) {
        GetToken();
        double dHold;
        Level4(dHold);
        Arith(op, dResult, dHold);
    }
}

void CExpression::Level4(double& dResult)
{
    char op;
    
    Level5(dResult);
    
    if ( (op = *m_pszToken) == '^' ) {
        GetToken();
        double dHold;
        Level4(dHold);
        Arith(op, dResult, dHold);
    }
}

void CExpression::Level5(double& dResult)
{
    char op = 0;
    
    if ( delimiter == m_chTokenType && '+' == *m_pszToken || '-' == *m_pszToken ) {
        op = *m_pszToken;
        GetToken();
    }
    
    Level6(dResult);
    
    if ( op ) {
        Unary(op, dResult);
    }
}

void CExpression::Level6(double& dResult)
{
    if ( '(' == *m_pszToken && delimiter == m_chTokenType ) {
        GetToken();
        Level2(dResult);
        if ( ')' != *m_pszToken ) {
            m_nErrorID = syntaxError;
            DisplayErrorMessage();
            return;
        }
        GetToken();
    }
    else {
        Primitive(dResult);
    }
}

void CExpression::Primitive(double& dResult)
{
    if ( number == m_chTokenType ) {
        dResult = atof(m_pszToken);
        /*
        char* pszStop;
        dResult = strtod(m_pszToken, &pszStop);
        if ( *pszStop ) {
            dResult = 0;
        }
        */
        GetToken();
    }
    else {
        m_nErrorID = syntaxError;
        DisplayErrorMessage();
    }
}

void CExpression::Arith(char op, double& dResult, double& dHold)
{
    double t, ex;
    
    switch ( op ) {
        case '-':
            dResult = dResult - dHold;
            break;
        case '+':
            dResult = dResult + dHold;
            break;
        case '*':
            dResult = dResult * dHold;
            break;
        case '/':
            if ( 0 == dHold ) {
                m_nErrorID = dividedByZero;
                DisplayErrorMessage();
                break;
            }
            dResult = dResult / dHold;
            break;
        case '%':
            t = dResult / dHold;
            dResult = dResult - t * dHold;
            break;
        case '^':
            ex = dResult;
            if ( 0 == dHold ) {
                dResult = 1;
            }
            else {
                for ( t = dHold-1; t > 0; --t ) {
                    dResult = dResult * ex;
                }
            }
            break;
        default:
            break;
    }
}

void CExpression::Unary(char op, double& dResult)
{
    if ( '-' == op ) {
        dResult = -dResult;
    }
}

void CExpression::SkipWhiteSpace(CString& strInput)
{
    // Create a buffer.
    ASSERT (strInput.GetLength() <= maxToken);
    char* p = strInput.GetBuffer(strInput.GetLength());
    
    char szTemp[maxToken+1];
    memset(szTemp, 0, sizeof(szTemp));

    int i = 0;
    while ( *p ) {
        if ( ' ' != *p && '\t' != *p && ',' != *p ) {
            szTemp[i++] = *p;
        }
        p++;
    }
    
    strInput = szTemp;
}

BOOL CExpression::IsDelimiter(const char ch, char* psz)
{
    while ( *psz ) {
        if ( *psz++ == ch ) {
            return TRUE;
        }
    }
    return FALSE;
}

void CExpression::DisplayErrorMessage(void)
{
    // Define the error message.
    char* pszErrorMsg[] = {
        "",
        "Syntax error.",
        "Divided by zero."
    };
    
    // Display the special error message.
    AfxMessageBox(pszErrorMsg[m_nErrorID]);
}

#endif  // _CHEN_
