// This file is part of the Coral project.
// Please read the copyright notice in the LICENSE file.

#include "io.h"
#include "xmi_writer_1.h"
#include "xmi_writer_2.h"
#include "xmi_reader_common.h"

#include <fstream>
#include <sstream>
#include <streambuf>
#include <coral/core/Project.h>

#include <coral/lib/safe_save_file.h>

NAMESPACE_BEGIN(coral);

NAMESPACE_BEGIN(io);

std::string saveDocument_str(const coral::Collection* coll,
			     const char* xmi_version)
	throw (save_error, std::exception) {
	std::ostringstream s;
	saveDocument_raw(s, coll, xmi_version);
	return s.str();
}

Element* loadModel(const char* filename,
		   Language* l) throw (load_error, std::exception) {
	if (!filename)
		throw load_error("Load error, no filename specified");
	return io_load_file(filename, l);
}

coral::autounref<coral::Project>
loadDocument(const char* filename,
	     Language* l) throw (load_error, std::exception) {
	if (!filename)
		throw load_error("Load error, no filename specified");
	return io_load_document(filename, l, false);
}

coral::autounref<Project>
loadDocument_str(const char* buffer) throw (load_error, std::exception) {
	if (!buffer)
		throw load_error("Load error, no memory buffer specified");
	if (buffer[0] == 0) return coral::autounref<Project>(NULL);
	return io_load_document(buffer, NULL, true);
}

void saveDocument_raw(std::ostream& out,
		      const coral::Collection* coll,
		      const char* xmi_version)
	throw (save_error, std::exception) {
	coral_assert(xmi_version);
	coral_assert(coll);

	std::ostream internal_out(out.rdbuf());
	internal_out.imbue(coral::clocale);

	if (!strcmp("1.2", xmi_version)) {
		XMIStreamer x;
		x.saveModel(coll, internal_out);
	} else if (!strcmp("2.0", xmi_version)) {
		XMIStreamer2 x;
		x.saveModel(coll, internal_out);
	} else {
		dlog << DEBUGLEVEL_DEBUG << "DEBUG: Bad file format: " << xmi_version << std::endl;
		throw save_error(std::string("Error saving. Bad file format \"") + xmi_version + "\"");
	}
}
		
void saveModel(const char* filename,
	       coral::Element* root,
	       const char* xmi_version)
	throw (save_error, std::exception) {
	coral_assert(root);
	Project* s = Project::getProject(root);
	autounref<Project> _s;
	if (!s) {
		_s = Project::getProjectByFilename(filename);
		s = _s.get();
	} else {
		s->ref();
		_s = s;
	}
	coral_assert(s);
	if (!xmi_version) {
		xmi_version
			= s->getXMIFormat().c_str();
	} else {
		s->setXMIFormat(xmi_version);
	}
	s->setFilename(filename);
	temp_ref t(root);
	s->contents()->reset();
	root->__setProject__(s);
	return saveDocument(s);
}

class output_xmi : public save_file {
	const char* m_xmi_version;
	Collection* m_contents;
public:
	output_xmi(const char* xmi_version,
		   Collection* contents)
		: save_file(),
		  m_xmi_version(xmi_version),
		  m_contents(contents) {
		;
		}
	virtual void operator()(std::ostream& out) {
		saveDocument_raw(out,
				 m_contents,
				 m_xmi_version);
	}
};

void saveDocument(coral::Project* st)
	throw (save_error, std::exception) {
	coral_assert(st);
#ifndef NDEBUG
	Iterator i(st->contents());
	ref_counted* _t;
	while ((_t = i.next()) != NULL) {
		Element* e = dynamic_cast<Element*>(_t);
		if (!e) continue;
		coral_assert(Project::getProject(e) == st);
	}
#endif

	output_xmi outputter(st->getXMIFormat().c_str(),
			     st->contents());

	safe_save_file(st->getFilename(),
		       outputter);

}

std::vector<std::string> getSupportedXMIVersions() {
	std::vector<std::string> s;
	s.push_back("1.2");
	s.push_back("2.0");
	return s;
}


ModelStreamer::ModelStreamer() { };
ModelStreamer::~ModelStreamer() throw () { };


NAMESPACE_END();
NAMESPACE_END();
