//
// $Id: Definition.cpp,v 1.12 2007/01/09 17:43:01 bakerj Exp $
//
//****************************************************************************************//
// Copyright (c) 2002-2007, 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 "Definition.h"

//DefinitionVector Definition::processedDefinitions;
DefinitionMap Definition::processedDefinitionsMap;

//****************************************************************************************//
//								Definition Class										  //	
//****************************************************************************************//
Definition::Definition() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Create a compelete Definition object
	//
	// -----------------------------------------------------------------------

	this->SetId("");
	this->SetCriteria(NULL);
	this->SetResult(OvalEnum::ERROR_RESULT);
	this->SetVariableInstance(1);
	this->SetVersion(1);
	this->SetWritten(false);
	this->SetAnalyzed(false);
}

Definition::~Definition() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Call delete on the criteria element
	//
	// -----------------------------------------------------------------------

	delete this->GetCriteria();
}

// ***************************************************************************************	//
//								 Public members												//
// ***************************************************************************************	//

Criteria* Definition::GetCriteria() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the criteria field's value
	//
	// -----------------------------------------------------------------------

	return this->criteria;
}

void Definition::SetCriteria(Criteria* criteria) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the criteria field's value
	//
	// -----------------------------------------------------------------------

	this->criteria = criteria;
}

string Definition::GetId() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the id field's value
	//
	// -----------------------------------------------------------------------

	return this->id;
}

void Definition::SetId(string id) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the id field's value
	//
	// -----------------------------------------------------------------------

	this->id = id;
}

OvalEnum::ResultEnumeration Definition::GetResult() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the result field's value
	//
	// -----------------------------------------------------------------------

	return this->result;
}

void Definition::SetResult(OvalEnum::ResultEnumeration result) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the result field's value
	//
	// -----------------------------------------------------------------------

	this->result = result;
}

int Definition::GetVariableInstance() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the variableInstance field's value
	//
	// -----------------------------------------------------------------------

	return this->variableInstance;
}

void Definition::SetVariableInstance(int variableInstance) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the variableInstance field's value
	//
	// -----------------------------------------------------------------------

	this->variableInstance = variableInstance;
}

int Definition::GetVersion() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the version field's value
	//
	// -----------------------------------------------------------------------

	return this->version;
}

void Definition::SetVersion(int version) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the version field's value
	//
	// -----------------------------------------------------------------------

	this->version = version;
}

bool Definition::GetWritten() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the written field's value
	//
	// -----------------------------------------------------------------------

	return this->written;
}

void Definition::SetWritten(bool written) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the written field's value
	//
	// -----------------------------------------------------------------------

	this->written = written;
}

bool Definition::GetAnalyzed() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the analyzed field's value
	//
	// -----------------------------------------------------------------------

	return this->analyzed;
}

void Definition::SetAnalyzed(bool analyzed) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the analyzed field's value
	//
	// -----------------------------------------------------------------------

	this->analyzed = analyzed;
}

Definition* Definition::SearchCache(string id) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	search the cache of definition for the specifed definition. 
	//	return NULL if not found
	//
	// -----------------------------------------------------------------------

	Definition* cachedDef = NULL;

	//DefinitionVector::iterator iterator;
	//for(iterator = Definition::processedDefinitions.begin(); iterator != Definition::processedDefinitions.end(); iterator++) {
	//	if((*iterator)->GetId().compare(id) == 0) {
	//		cachedDef = (*iterator);
	//		break;
	//	}
	//}

	DefinitionMap::iterator iterator;
	iterator = Definition::processedDefinitionsMap.find(id);
	if(iterator != Definition::processedDefinitionsMap.end()) {
		cachedDef = iterator->second;
	} 

	return cachedDef;
}

void Definition::ClearCache() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	clear the cahce of processed defintions
	// -----------------------------------------------------------------------

	//Definition* def = NULL;
	//while(Definition::processedDefinitions.size() != 0) {
	//  	def = Definition::processedDefinitions[Definition::processedDefinitions.size()-1];
	//  	Definition::processedDefinitions.pop_back();
	//  	delete def;
	//  	def = NULL;
	//}

	DefinitionMap::iterator iterator;
	for(iterator = Definition::processedDefinitionsMap.begin(); iterator != Definition::processedDefinitionsMap.end(); iterator++) {
		
		Definition* def = iterator->second;
		delete def;
	}
	
	Definition::processedDefinitionsMap.clear();
}

void Definition::Write(DOMElement* parentElm) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	writes a Definition element
	//	calls criteria->Write() 
	//
	// -----------------------------------------------------------------------

	if(!this->GetWritten()) {
		this->SetWritten(true);

		// get the parent document
		XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* resultDoc = parentElm->getOwnerDocument();

		// create a new definition element
		DOMElement* definitionElm = XmlCommon::AddChildElement(resultDoc, parentElm, "definition");

		// add the attributes
		XmlCommon::AddAttribute(definitionElm, "definition_id", this->GetId());
		XmlCommon::AddAttribute(definitionElm, "version", Common::ToString(this->GetVersion()));
		XmlCommon::AddAttribute(definitionElm, "result", OvalEnum::ResultToString(this->GetResult()));

		if(this->GetVariableInstance() != 1) {
			XmlCommon::AddAttribute(definitionElm, "variable_instance", Common::ToString(this->GetVariableInstance()));
		}	

		// write the criteria
		if(this->GetCriteria() != NULL) {
			this->GetCriteria()->Write(definitionElm);
		}
	}
}

void Definition::Parse(DOMElement* definitionElm) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Make sure a definition is only parsed once.
	//	parses Definition elm to a Definition obj
	//	calls Criteria->Parse() on the Criteria elm
	//
	// -----------------------------------------------------------------------

	// get id
	string id = XmlCommon::GetAttributeByName(definitionElm, "id");

	// get the attributes
	this->SetId(XmlCommon::GetAttributeByName(definitionElm, "id"));
	string versionStr = XmlCommon::GetAttributeByName(definitionElm, "version");
	int versionInt = atoi(versionStr.c_str());
	this->SetVersion(versionInt);
	
	// parse the criteria
	DOMElement* criteriaElm = XmlCommon::FindElementNS(definitionElm, "criteria");
	
	if(criteriaElm != NULL) {
		this->SetCriteria(new Criteria());	
		this->GetCriteria()->Parse(criteriaElm);
	} else {
		this->SetAnalyzed(true);
		this->SetResult(OvalEnum::NOT_EVALUATED_RESULT);
	}

	Definition::processedDefinitionsMap.insert(DefinitionPair(this->GetId(), this));
}

OvalEnum::ResultEnumeration Definition::Analyze() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	make sure not previously analyzed.
	//	calls Criteria->Analyze()
	//	saves and returns the result
	// -----------------------------------------------------------------------

	if(!this->GetAnalyzed()) {

		// analyze the definition and save the result
		OvalEnum::ResultEnumeration currentResult = this->GetCriteria()->Analyze();
		this->SetResult(currentResult);

		this->SetAnalyzed(true);

		// make sure the results are stored only once
		StringPair* pair = new StringPair();
		pair->first = this->GetId();
		pair->second = OvalEnum::ResultToString(currentResult);
		if(result == OvalEnum::TRUE_RESULT) {
			Analyzer::AppendTrueResult(pair);
		} else if(result == OvalEnum::FALSE_RESULT) {
			Analyzer::AppendFalseResult(pair);
		} else if(result == OvalEnum::UNKNOWN_RESULT) {
			Analyzer::AppendUnknownResult(pair);
		} else if(result == OvalEnum::NOT_APPLICABLE_RESULT) {
			Analyzer::AppendNotApplicableResult(pair);
		} else if(result == OvalEnum::NOT_EVALUATED_RESULT) {
			Analyzer::AppendNotEvaluatedResult(pair);
		} else if(result == OvalEnum::ERROR_RESULT) {
			Analyzer::AppendErrorResult(pair);
		}
	}

	return this->GetResult();
}
