/////////////////////////////////////////////////////////////////////
//
// CFX_COMPUTESTATISTICS - Cold Fusion custom tag
//
// Copyright 2002. All Rights Reserved.
//


#include "stdafx.h"  // Standard MFC libraries
#include "cfx.h"     // CFX Custom Tag API
#include "math.h"

void ProcessTagRequest( CCFXRequest* pRequest ) 
{
  try
  {
    /* BEGIN ATTRIBUTE VALIDATION */
     
    // Make sure a VARIABLE attribute was actually passed to the tag
    if ( pRequest->AttributeExists("VARIABLE")==FALSE ) {
      pRequest->ThrowException(
        "Error in CFX_ComputeStatistics Tag", 
        "No VARIABLE attribute specified.");
    }
    
    // Retrieve value of VARIABLE attribute
    CString lpszVariable = pRequest->GetAttribute("VARIABLE") ;


    // Make sure a VARIABLE attribute was actually passed to the tag
    if ( pRequest->AttributeExists("COLUMN")==FALSE ) {
      pRequest->ThrowException(
        "Error in CFX_ComputeStatistics Tag", 
        "No COLUMN attribute specified.");
    }
    
    // Retrieve value of VARIABLE attribute
    LPCSTR lpszColumn = pRequest->GetAttribute("COLUMN") ;

    // Retrieve the query specified in the tag's QUERY attribute
    CCFXQuery* pQuery = pRequest->GetQuery();    

    // Make sure a query was actually passed to the tag
    if ( pQuery == NULL ) {
      pRequest->ThrowException(
        "Error in CFX_ComputeStatistics Tag", 
        "No QUERY provided.");
    }


    // Find the index position of the column named in the COLUMN attribute
    int colIndex = pQuery->GetColumns()->GetIndexForString( lpszColumn );

    // If the column specified in COLUMN attribute does not exist in query
    if ( colIndex == CFX_STRING_NOT_FOUND ) {
      pRequest->ThrowException(
        "Error in CFX_ComputeStatistics Tag", 
        "The COLUMN attribute does not match up to a column in the query.");
    }

    /* FINISHED WITH ATTRIBUTE VALIDATION */



    // Local variables
    double dTotal = 0;
    double dMean = 0;
    double dStdDev = 0;
    double dSumSq = 0;
    double dVariance = 0;
    int iValueCount = 0;


    // Compute the total of all the numbers in the data set
    for ( int Row = 1; Row <= pQuery->GetRowCount(); Row++) {

      // Get the value (as a string) from the query
      LPCSTR thisVal = pQuery->GetData(Row, colIndex);
      
      // Assuming the value is not an empty string
      if ( strlen(thisVal) > 0) {
        // Add this value to the total
        dTotal += atof(thisVal);

        // Increment the counter of non-null values
        iValueCount++;


      // Include optional debug messages for skipped rows
      } else if ( pRequest->Debug() ) {
        char buffer[50];
        sprintf(buffer, "Skipping row %i because value is empty/NULL.", Row);
        pRequest->WriteDebug(buffer);
      
      }
    }


    // The rest of the computations will be dividing by iValueCount,
    // to check it to protect ourselves against a divide-by-zero situation
    if ( iValueCount > 0 ) {

      // Compute the mean (average) of the numbers
      dMean = dTotal / iValueCount;

      // Begin calculating the variance
      for ( Row = 1; Row <= pQuery->GetRowCount(); Row++) {
        // Get the value (as a string) from the query
        LPCSTR thisVal = pQuery->GetData(Row, colIndex);
        // Assuming the value is not an empty string
        if ( strlen(thisVal) > 0) {
          dSumSq += pow( dMean - atof(thisVal), 2);
        }
      }
      // Finish computing the variance
      dVariance = dSumSq / iValueCount;

      // The standard deviation is the square root of the variance
      dStdDev = sqrt(dVariance);

    }



    // Return statistics to the calling ColdFusion page
    char result[20];
    gcvt(dStdDev, 10, result);
    pRequest->SetVariable(lpszVariable + ".StandardDeviation", result);
    gcvt(dVariance, 10, result);
    pRequest->SetVariable(lpszVariable + ".Variance", result);
    gcvt(dMean, 10, result);
    pRequest->SetVariable(lpszVariable + ".Mean", result);
    gcvt(dTotal, 10, result);
    pRequest->SetVariable(lpszVariable + ".Sum", result);
    gcvt(iValueCount, 10, result);
    pRequest->SetVariable(lpszVariable + ".Count", result);
    
  }

  // Catch Cold Fusion exceptions & re-raise them
  catch( CCFXException* e )
  {
    pRequest->ReThrowException( e ) ;
  }
  
  // Catch ALL other exceptions and throw them as 
  // Cold Fusion exceptions (DO NOT REMOVE! -- 
  // this prevents the server from crashing in 
  // case of an unexpected exception)
  catch( ... )
  {
    pRequest->ThrowException( 
      "Error occurred in tag CFX_ComputeStatistics",
      "Unexpected error occurred while processing tag." ) ;
  }

}