//
// $Id: AbsDataCollector.cpp,v 1.5 2006/09/20 13:56:02 bakerj Exp $
//
//****************************************************************************************//
// Copyright (c) 2006, 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 "AbsDataCollector.h"

//****************************************************************************************//
//								AbsDataCollector Class									  //	
//****************************************************************************************//
AbsDataCollector* AbsDataCollector::instance = NULL;
bool AbsDataCollector::isRunning = false;

AbsDataCollector::AbsDataCollector() {
	this->collectedObjectsElm = NULL;
	this->systemDataElm = NULL;	
}

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

	delete(this->objectCollector);
}

// ***************************************************************************************	//
//									Public members											//
// ***************************************************************************************	//
AbsDataCollector* AbsDataCollector::Instance() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return a reference to the singleton ovaldatacollector instance
	// -----------------------------------------------------------------------

	return AbsDataCollector::instance;
}

DOMElement* AbsDataCollector::GetSCCollectedObjectsElm() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return a reference to the collected_objects element in the sc document
	// -----------------------------------------------------------------------

	if(this->collectedObjectsElm == NULL) {
		//	Create the collected_objects element and add it as a child of the sc element
		DOMElement* scNode = XmlCommon::FindElement(DocumentManager::GetSystemCharacterisitcsDocument(), "oval_system_characteristics");
		DOMElement *elm = XmlCommon::AddChildElement(DocumentManager::GetSystemCharacterisitcsDocument(), scNode, "collected_objects"); 
		this->collectedObjectsElm = elm;
	}
	return this->collectedObjectsElm;
}

DOMElement* AbsDataCollector::GetSCSystemDataElm() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return a reference to the system data element in the sc document
	// -----------------------------------------------------------------------

	if(this->systemDataElm == NULL) {
		//	Create the system_data element and add it as a child of the sc element
		DOMElement* scNode = XmlCommon::FindElement(DocumentManager::GetSystemCharacterisitcsDocument(), "oval_system_characteristics");
		DOMElement *elm = XmlCommon::AddChildElement(DocumentManager::GetSystemCharacterisitcsDocument(), scNode, "system_data"); 
		this->systemDataElm = elm;
	}
	return this->systemDataElm;
}

void AbsDataCollector::InitBase(AbsObjectCollector* objectCollector) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	initilaize the base AbsDataCollector
	// -----------------------------------------------------------------------

	string errMsg;

	if(objectCollector != NULL) 
		this->objectCollector = objectCollector;
	else
		throw AbsDataCollectorException("Error: The specified objectCollector is NULL. Unable to initialize the data collector.");

	//	Get the system_characteristics node
	DOMElement* scNode = XmlCommon::FindElement(DocumentManager::GetSystemCharacterisitcsDocument(), "oval_system_characteristics");

	//	Add the namespace info to the oval_system_characteristics node
	XmlCommon::AddXmlns(DocumentManager::GetSystemCharacterisitcsDocument(), "http://oval.mitre.org/XMLSchema/oval-common-5", "oval");
	XmlCommon::AddXmlns(DocumentManager::GetSystemCharacterisitcsDocument(), "http://oval.mitre.org/XMLSchema/oval-definitions-5", "oval-def");
	XmlCommon::AddXmlns(DocumentManager::GetSystemCharacterisitcsDocument(), "http://oval.mitre.org/XMLSchema/oval-system-characteristics-5", "oval-sc");
	XmlCommon::AddXmlns(DocumentManager::GetSystemCharacterisitcsDocument(), "http://www.w3.org/2001/XMLSchema-instance", "xsi");
	XmlCommon::AddSchemaLocation(DocumentManager::GetSystemCharacterisitcsDocument(), "http://oval.mitre.org/XMLSchema/oval-definitions-5 oval-definitions-schema.xsd");
	XmlCommon::AddSchemaLocation(DocumentManager::GetSystemCharacterisitcsDocument(), "http://oval.mitre.org/XMLSchema/oval-system-characteristics-5 oval-system-characteristics-schema.xsd");
	XmlCommon::AddSchemaLocation(DocumentManager::GetSystemCharacterisitcsDocument(), "http://oval.mitre.org/XMLSchema/oval-common-5 oval-common-schema.xsd");

	// Add Generator Info
	this->WriteGenerator();

	//	Create the system_info element and add it as a child of the sc element
	DOMElement *sysInfoElm = XmlCommon::CreateElement(DocumentManager::GetSystemCharacterisitcsDocument(), "system_info"); 
	scNode->appendChild(sysInfoElm);

	// Write system info
	this->WriteSystemInfo();
}

void AbsDataCollector::AddXmlns(string newXmlnsAlias, string newXmlnsUri) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add the specified schmema location tot he document root. Ensure that 
	//	The specified schema location is not already added
	// -----------------------------------------------------------------------

	XmlCommon::AddXmlns(DocumentManager::GetSystemCharacterisitcsDocument(), newXmlnsUri, newXmlnsAlias);
}

void AbsDataCollector::AddSchemaLocation(string newSchemaLocation) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add the specified schmema location tot he document root. Ensure that 
	//	The specified schema location is not already added
	//
	// -----------------------------------------------------------------------

	XmlCommon::AddSchemaLocation(DocumentManager::GetSystemCharacterisitcsDocument(), newSchemaLocation);
}

void AbsDataCollector::Run() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Loop through all tests in the provided oval document.
	//	Get the object reference fromthe test and call the object collector
	//	
	// -----------------------------------------------------------------------

	AbsDataCollector::isRunning = true;
	//////////////////////////////////////////////////////
	////////////////  Process OVAL Tests  ////////////////
	//////////////////////////////////////////////////////
	//	get a ptr to the tests node in the oval document.
	DOMElement* objectsNode = XmlCommon::FindElementNS(DocumentManager::GetDefinitionDocument(), "objects");
	//	get a list of the child nodes
	DOMNodeList* ovalObjectsChildren = objectsNode->getChildNodes();

	if(!Log::WriteToScreen())
		cout << "      Collecting object:  "; 

	//	Loop through all the nodes in ovalTestList
	int prevIdLength = 1;
	int curIdLength = 1;
	unsigned int index = 0;
	while(index < ovalObjectsChildren->getLength()) {
        DOMNode *tmpNode = ovalObjectsChildren->item(index);

		//	only concerned with ELEMENT_NODEs
		if (tmpNode->getNodeType() == DOMNode::ELEMENT_NODE) {
			DOMElement *object = (DOMElement*)tmpNode;

			string objectId = XmlCommon::GetAttributeByName(object, "id");
			
			Log::Debug("Collecting object id: " + objectId);

			if(!Log::WriteToScreen()) {
				curIdLength = objectId.length();
				string blankSpaces = "";
				if(prevIdLength > curIdLength)
					blankSpaces = Common::PadStringWithChar(blankSpaces, ' ', prevIdLength-curIdLength);

				string backSpaces = "";
				backSpaces = Common::PadStringWithChar(backSpaces, '\b', prevIdLength);
				string endBackSpaces = "";
				endBackSpaces = Common::PadStringWithChar(endBackSpaces, '\b', blankSpaces.length());
				cout << backSpaces << objectId << blankSpaces << endBackSpaces;
			}

			this->objectCollector->Run(objectId);

			prevIdLength = curIdLength;
		}

		index ++;
	}

	if(!Log::WriteToScreen()) {
		string fin = " FINISHED ";
		int curLen = fin.length();
		string blankSpaces = "";
		if(prevIdLength > curLen)
			blankSpaces = Common::PadStringWithChar(blankSpaces, ' ', prevIdLength-curLen);
		string backSpaces = "";
		backSpaces = Common::PadStringWithChar(backSpaces, '\b', prevIdLength);
		
		cout << backSpaces << fin << blankSpaces << endl;
	}

	// Once finished running call write method on all collected objects
	CollectedObject::WriteCollectedObjects();
	AbsDataCollector::isRunning = false;
	Filter::ClearCache(); // TODO - not currently in use.
	AbsVariable::ClearCache();
	AbsProbe::ClearGlobalCache();
	VariableValue::ClearCache();
}

bool AbsDataCollector::GetIsRunning(){
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return teh static isRunning flag
	//
	// -----------------------------------------------------------------------

	return AbsDataCollector::isRunning;
}

void AbsDataCollector::WriteGenerator() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Write the generator element. Note that the values are all hard coded
	//	except for the time stamp value.
	//
	// -----------------------------------------------------------------------

	DOMElement *scNode = XmlCommon::FindElement(DocumentManager::GetSystemCharacterisitcsDocument(), "oval_system_characteristics");
	DOMElement *generatorElm = XmlCommon::AddChildElement(DocumentManager::GetSystemCharacterisitcsDocument(), scNode, "generator");
	XmlCommon::AddChildElement(DocumentManager::GetSystemCharacterisitcsDocument(), generatorElm, "oval:product_name", "OVAL Definition Interpreter");
	XmlCommon::AddChildElement(DocumentManager::GetSystemCharacterisitcsDocument(), generatorElm, "oval:product_version", Version::GetVersion());
	XmlCommon::AddChildElement(DocumentManager::GetSystemCharacterisitcsDocument(), generatorElm, "oval:schema_version", "5.0");
	XmlCommon::AddChildElement(DocumentManager::GetSystemCharacterisitcsDocument(), generatorElm, "oval:timestamp", Common::GetTimeStamp());
}

//****************************************************************************************//
//						AbsDataCollectorException Class								  //	
//****************************************************************************************//
AbsDataCollectorException::AbsDataCollectorException(string errMsgIn, int severity, Exception* ex) : Exception(errMsgIn, severity, ex) {
	// -----------------------------------------------------------------------
	//	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 that 
	//	takes a single string param.
	// -----------------------------------------------------------------------
}

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

