///////////////////////////////////////////////////////////////////////////////
// 
//  Copyright (2013) Alexander Stukowski
//
//  This file is part of OVITO (Open Visualization Tool).
//
//  OVITO is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  OVITO is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////////////

#include <core/Core.h>
#include <core/scene/objects/DataObject.h>
#include <core/scene/ObjectNode.h>
#include <core/scene/pipeline/PipelineObject.h>
#include <core/dataset/DataSetContainer.h>

namespace Ovito { OVITO_BEGIN_INLINE_NAMESPACE(ObjectSystem) OVITO_BEGIN_INLINE_NAMESPACE(Scene)

IMPLEMENT_SERIALIZABLE_OVITO_OBJECT(DataObject, RefTarget);
DEFINE_PROPERTY_FIELD(DataObject, saveWithScene, "SaveWithScene");
DEFINE_FLAGS_VECTOR_REFERENCE_FIELD(DataObject, displayObjects, "DisplayObjects", DisplayObject, PROPERTY_FIELD_NEVER_CLONE_TARGET);
SET_PROPERTY_FIELD_LABEL(DataObject, saveWithScene, "Save data with scene");
SET_PROPERTY_FIELD_LABEL(DataObject, displayObjects, "Display objects");

/******************************************************************************
* Constructor.
******************************************************************************/
DataObject::DataObject(DataSet* dataset) : RefTarget(dataset), _revisionNumber(0), _saveWithScene(true)
{
	INIT_PROPERTY_FIELD(saveWithScene);
	INIT_PROPERTY_FIELD(displayObjects);
}

/******************************************************************************
* Asks the object for the result of the data pipeline.
******************************************************************************/
PipelineFlowState DataObject::evaluateImmediately(const PipelineEvalRequest& request) 
{
	return PipelineFlowState(this, objectValidity(request.time()));
}

/******************************************************************************
* Asks the object for the result of the data pipeline.
******************************************************************************/
Future<PipelineFlowState> DataObject::evaluateAsync(const PipelineEvalRequest& request) 
{
	return Future<PipelineFlowState>::createImmediate(PipelineFlowState(this, objectValidity(request.time())));
}

/******************************************************************************
* Sends an event to all dependents of this RefTarget.
******************************************************************************/
void DataObject::notifyDependents(ReferenceEvent& event)
{
	// Automatically increment revision counter each time the object changes.
	if(event.type() == ReferenceEvent::TargetChanged)
		_revisionNumber++;

	RefTarget::notifyDependents(event);
}

/******************************************************************************
* Handles reference events sent by reference targets of this object.
******************************************************************************/
bool DataObject::referenceEvent(RefTarget* source, ReferenceEvent* event)
{
	// Do not propagate change messages generated by the display objects.
	if(event->type() == ReferenceEvent::TargetChanged || event->type() == ReferenceEvent::PendingStateChanged) {
		if(displayObjects().contains(static_cast<DisplayObject*>(source)))
			return false;
	}

	if(event->type() == ReferenceEvent::TargetChanged) {
		// Automatically increment revision counter each time a sub-object of this object changes.
		_revisionNumber++;
	}

	return RefTarget::referenceEvent(source, event);
}

/******************************************************************************
* Saves the class' contents to the given stream.
******************************************************************************/
void DataObject::saveToStream(ObjectSaveStream& stream)
{
	RefTarget::saveToStream(stream);
	stream.beginChunk(0x02);
	stream.endChunk();
}

/******************************************************************************
* Loads the class' contents from the given stream.
******************************************************************************/
void DataObject::loadFromStream(ObjectLoadStream& stream)
{
	RefTarget::loadFromStream(stream);
	int formatVersion = stream.expectChunkRange(0, 0x02);
	if(formatVersion == 0x01) {
		OORef<DisplayObject> displayObject = stream.loadObject<DisplayObject>();
		if(displayObject) {
			_displayObjects.clear();
			addDisplayObject(displayObject);
		}
	}
	stream.closeChunk();
}

/******************************************************************************
* Returns whether the internal data is saved along with the scene.
******************************************************************************/
bool DataObject::saveWithScene() const
{
	// Check if 'Save with scene' is disabled for any of the parent objects.
	for(RefMaker* dependent : this->dependents()) {
		if(DataObject* parent = dynamic_object_cast<DataObject>(dependent)) {
			if(!parent->saveWithScene())
				return false;
		}
	}

	return _saveWithScene;
}

/******************************************************************************
* Returns a list of object nodes that have this object as a data source.
******************************************************************************/
QSet<ObjectNode*> DataObject::dependentNodes() const
{
	QSet<ObjectNode*> nodeList;
	for(RefMaker* dependent : this->dependents()) {
		if(ObjectNode* node = dynamic_object_cast<ObjectNode>(dependent)) {
			if(node->dataProvider() == this)
				nodeList.insert(node);
		}
		else if(PipelineObject* pipeline = dynamic_object_cast<PipelineObject>(dependent)) {
			if(pipeline->sourceObject() == this)
				nodeList.unite(pipeline->dependentNodes());
		}
	}
	return nodeList;
}

OVITO_END_INLINE_NAMESPACE
OVITO_END_INLINE_NAMESPACE
}	// End of namespace
