//
// $Id: Analyzer.cpp,v 1.12 2005/09/27 19:05:54 bakerj Exp $
//
//****************************************************************************************//
// Copyright (c) 2005, The MITRE Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice, this list
//       of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice, this 
//       list of conditions and the following disclaimer in the documentation and/or other
//       materials provided with the distribution.
//     * Neither the name of The MITRE Corporation nor the names of its contributors may be
//       used to endorse or promote products derived from this software without specific 
//       prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
// SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//****************************************************************************************//

#include "Analyzer.h"

//****************************************************************************************//
//								Analyzer Class											  //	
//****************************************************************************************//
OvalResultXml* Analyzer::resultWriter = NULL;
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* Analyzer::dataDoc = NULL;
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* Analyzer::mappingDoc = NULL;
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* Analyzer::ovalDoc = NULL;
bool Analyzer::useConfiguration = true;
bool Analyzer::initialized = false;
string Analyzer::family = "";

Analyzer::Analyzer()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Ensure that the init method has been called
	//	Instantiate a new Regex object
	// -----------------------------------------------------------------------
	
	if(!initialized)
		throw AnalyzerException("Error: Before instantiating an Analyzer a call to Init must first be made.", ERROR_FATAL);

	myMatcher = new REGEX();
}

Analyzer::~Analyzer()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	
	//
	// -----------------------------------------------------------------------

	delete myMatcher;
	myMatcher = NULL;
}

// ***************************************************************************************	//
//								Public members												//
// ***************************************************************************************	//
Result Analyzer::CombineResultsByCheck(iVector *results, string check)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return a result value based on combining the results in the 
	//	provided array and considering the provided check value
	//
	// -----------------------------------------------------------------------

	Result combinedResult = unknown_result;

	// Validate input check string 
	if(check.compare("all") != 0 && 
		check.compare("at least one") != 0 &&
		check.compare("none exist") != 0 &&
		check.compare("only one") != 0) 
	{
		throw AnalyzerException("Error: An unsupported value for check was specified. Check: " + check, ERROR_FATAL);
	}

	// Make sure the results vector has items. Return unknown_result if it is empty
	if(results->size() == 0) {
		return unknown_result;
	}

	// Get counts of result values
	int trueCount = 0;
	int falseCount = 0;
	int unknownCount = 0;
	iVector::iterator result;
	for (result=results->begin(); result!=results->end(); result++) {

		if((*result) == true_result) {
			trueCount++;
		} else if((*result) == false_result) {
			falseCount++;
		} else {
			unknownCount++;
		}
	}

	// Set the combined result
	if(check.compare("all") == 0) {
		if(trueCount > 0 && falseCount == 0 && unknownCount == 0) {
			combinedResult = true_result;
		} else if(falseCount > 0) {
			combinedResult = false_result;
		} else if(falseCount == 0 && unknownCount > 0) {
			combinedResult = unknown_result;
		}
	} else if(check.compare("at least one") == 0) {
		if(trueCount > 0) {
			combinedResult = true_result;
		} else if(trueCount == 0 && unknownCount == 0 && falseCount > 0) {
			combinedResult = false_result;
		} else if(trueCount == 0 && unknownCount > 0) {
			combinedResult = unknown_result;
		}
	} else if(check.compare("none exist") == 0) {
		if(trueCount == 0 && unknownCount == 0) {
			combinedResult = true_result;
		} else if(trueCount > 0) {
			combinedResult = false_result;
		} else if(trueCount == 0 && unknownCount > 0) {
			combinedResult = unknown_result;
		}
	} else if(check.compare("only one") == 0) {
		if(trueCount == 1 && unknownCount == 0) {
			combinedResult = true_result;
		} else if((trueCount > 1) || (trueCount == 0 && unknownCount == 0 && falseCount > 0)) {
			combinedResult = false_result;
		} else if(trueCount < 2 && unknownCount > 0) {
			combinedResult = unknown_result;
		}
	}

	return combinedResult;
}

Result Analyzer::CombineResultsByOperation(iVector *results, string operation)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return a sinagle result value based on combining the results in the 
	//	provided vector using the provided operation. If the provided vector 
	//  is empty return an unknown result.
	//
	// -----------------------------------------------------------------------

	// Validate input check string 
	if(operation.compare("AND") != 0 && 
		operation.compare("OR") != 0 &&
		operation.compare("XOR") != 0) 
	{
		throw AnalyzerException("Error: An unsupported value for operation was specified. Operation: " + operation, ERROR_FATAL);
	}

	// Make sure the results vector has items. Return unknown_result if it is empty
	if(results->size() == 0) {
		return unknown_result;
	}

	// Get counts of result values
	int trueCount = 0;
	int falseCount = 0;
	int unknownCount = 0;
	iVector::iterator result;
	for (result=results->begin(); result!=results->end(); result++) {

		if((*result) == true_result) {
			trueCount++;
		} else if((*result) == false_result) {
			falseCount++;
		} else {
			unknownCount++;
		}
	}

	// Set the combined result - default to unknown
	Result combinedResult = unknown_result;
	if(operation.compare("AND") == 0) {
		if(trueCount > 0 && falseCount == 0 && unknownCount == 0) {
			combinedResult = true_result;
		} else if(falseCount > 0) {
			combinedResult = false_result;
		} else if(falseCount == 0 && unknownCount > 0) {
			combinedResult = unknown_result;
		}
	} else if(operation.compare("OR") == 0) {
		if(trueCount > 0) {
			combinedResult = true_result;
		} else if(trueCount == 0 && unknownCount == 0 && falseCount > 0) {
			combinedResult = false_result;
		} else if(trueCount == 0 && falseCount == 0 && unknownCount > 0) {
			combinedResult = unknown_result;
		}
	} else if(operation.compare("XOR") == 0) {
		if(unknownCount > 0) {
			combinedResult = unknown_result;
		} else if(trueCount%2 == 1) {
			combinedResult = true_result;
		} else {
			combinedResult = false_result;
		}
	}

	return combinedResult;
}

void Analyzer::Init(OvalResultXml *resultWriterIn, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *dataDocIn, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *ovalDocIn, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *mapppingDocIn, bool useConfig, string familyIn)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Initialize the static members of the class
	//
	// -----------------------------------------------------------------------

	Analyzer::dataDoc = dataDocIn;
	Analyzer::mappingDoc = mapppingDocIn;
	Analyzer::ovalDoc = ovalDocIn;
	Analyzer::resultWriter = resultWriterIn;
	Analyzer::useConfiguration = useConfig;
	Analyzer::initialized = true;
	Analyzer::family = familyIn;
}

// ***************************************************************************************	//
//								Private members												//
// ***************************************************************************************	//
Result Analyzer::GetTestResult(string testId) 
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the result of the already processed test
	//
	// -----------------------------------------------------------------------

	Result result = unknown_result;
	bool foundTest = false;

	trVector::iterator testResult;
	for (testResult=processedTests.begin(); testResult!=processedTests.end(); testResult++) {
		
		if((*testResult)->GetTestId().compare(testId) == 0) {
			result = (*testResult)->GetResult();
			foundTest = true;
		}
	}

	if(!foundTest)
		throw AnalyzerException("Error: The specified test has not yet been processed. id: " + testId , ERROR_FATAL);

	return result;
}

Result Analyzer::NegateResult(Result resultIn)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Negate the specified result value and return it
	// -----------------------------------------------------------------------

	Result negatedResult;

	switch(resultIn) {
	
	case true_result:
		negatedResult = false_result;
		break;
	case false_result:
		negatedResult = true_result;
		break;
	default:
		negatedResult = unknown_result;
		break;
	}

	return negatedResult;
}

bool Analyzer::Processed(string testId) 
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return true iff the specified test has been processed already
	//
	// -----------------------------------------------------------------------

	bool isProcessed = false;

	trVector::iterator testResult;
	for (testResult=processedTests.begin(); testResult!=processedTests.end(); testResult++) {
		
		if((*testResult)->GetTestId().compare(testId) == 0) {
			isProcessed = true;
			break;
		}
	}

	return isProcessed;
}

string Analyzer::ResultToString(Result result) 
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Store the result for the test.
	//
	// -----------------------------------------------------------------------

	string strResult = "";

	switch (result)
	{
		case unknown_result: 
			strResult = "-1";
			break;
		case true_result:
			strResult = "1";
			break;
		case false_result:
			strResult = "0";
			break;
		default:
			throw AnalyzerException("Error: SwitchDataType() Unsupported result specified.");
			break;
	}

	return strResult;
}


void Analyzer::Store(string testId, Result result) 
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Store the result for the test.
	//
	// -----------------------------------------------------------------------

	this->processedTests.push_back(new TestResult(testId, result));
}

//****************************************************************************************//
//								AnalyzerException Class									  //	
//****************************************************************************************//
AnalyzerException::AnalyzerException(string errMsgIn, int severity) : Exception(errMsgIn, severity)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Set the error message and then set the severity to ERROR_FATAL. This is 
	//	done with the explicit call to the Exception class constructor
	//
	// -----------------------------------------------------------------------

}

AnalyzerException::~AnalyzerException()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Do nothing for now
	//
	// -----------------------------------------------------------------------

}


