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

#include <coral/coral.h>
#include <coral/lib/multithread.h>

#include <coral/core/CorePrimitives.h>
#include <coral/lang/XMI/XMI.h>
#include <coral/lang/SMD01/SMD01.h>
#include <coral/lib/unicodestring.h>

#include <coral/core/config.h>
#include <coral/core/Project.h>
#include <stdlib.h>

#include <signal.h>
#include <unistd.h>

#ifdef CFG_HAS_BACKTRACE
#include <execinfo.h>
#endif

#include <Python.h>
#include <compile.h>
#include <frameobject.h>

#include <coral/core/config.h>
#include <coral/core/xmlns_conversion_table.h>

#include <coral/core/io/io.h>
#include <coral/lang/SMD01/smd2lang.h>

namespace coral {

	std::locale clocale("C");

	NAMESPACE_BEGIN();

#ifdef CFG_HAS_BACKTRACE
	void print_c_backtrace() throw () {

		void* buf[128];
		int n = backtrace(buf, 128);
		write(STDERR_FILENO,
		      "**********************************************************************\n"
		      "* Backtrace (for Coral version ",
		      71+31);
		write(STDERR_FILENO, coral::version, strlen(coral::version));
		write(STDERR_FILENO, ")\n", 2);
		backtrace_symbols_fd(buf, n, STDERR_FILENO);
		write(STDERR_FILENO,
		      "**********************************************************************\n",
		      71);
	}
#else
	void print_c_backtrace() throw () { ; }
#endif

	void print_python_backtrace() throw () {
		PyThreadState* cur = PyThreadState_Get();
		if (!cur) return;
		PyFrameObject* frame = cur->frame;
		if (!frame) return;
		for (;;) {
			PyCodeObject* code = frame->f_code;
			if (code) {
				int line =
					PyCode_Addr2Line(code, frame->f_lasti);
				PyObject* pyfilename = code->co_filename;
				const char* filename
					= PyString_AsString(pyfilename);
				dlog
					<< filename << ": Line " << line
					<< std::endl;
			}
			frame = frame->f_back;
			if (!frame) break;
		}
	}

	void sighandler_SEGV(int sig) throw () {
		write(STDERR_FILENO,
		      "**********************************************************************\n"
		      "* Segmentation fault                                                  \n",
		      71+71);
		print_backtrace();
		write(STDERR_FILENO,
		      "Exiting Coral.\n",
		      16);
		_exit(123);
	}

	/*
	 * Sometimes we want the real one, but we let users
	 * disable it via an environment variable
	 * CORALINSTALLATIONPATH
	 */
	void fixInstallationPath() throw () {
		const char* inst_path = getenv("CORALINSTALLATIONPATH");
		if (inst_path) {
			installation_path = inst_path;
		}
	}

	void fixDebugLevel() throw () {
		const char* dlevel = getenv("CORALDEBUG");
		if (dlevel) {
			const char** p = debuglevel_str;
			int d = 0;
			coral_assert(DEBUGLEVEL_ALL == 0);
			while (*p) {
				if (!strcmp(dlevel, *p)) {
					setDebugLevel(static_cast<debuglevel>(d));
					break;
				}
				++p;
				++d;
			}
		}
	}

	void fixCoralPath() {
		char* _cpath = getenv("CORALPATH");
		std::vector<std::string> new_coral_path;
		if (_cpath) {
			std::string cpath = _cpath;
			std::string::size_type pos = 0;
			for (;;) {
				std::string::size_type newpos
					= cpath.find(':', pos);
				std::string s = cpath.substr(pos, newpos);
				/*
				 * Don't obey the 'standard'
				 * "empty string means current directory"
				 * It just confuses people. So, use a single
				 * dot for that.
				 */
				if (!s.empty())
					new_coral_path.push_back(s);
				if (newpos == std::string::npos)
					break;
				pos = newpos + 1;
			}
		}
		/*
		 * Always add installation path
		 */
		new_coral_path.push_back(installation_path);

		/*
		 * Always add HOME directory
		 * A bit dubious, but works most of the time, so
		 * it's convenient enough.
		 */
		const char* chome = getenv("HOME");
		if (chome) {
			new_coral_path.push_back(chome);
			/*
			 * Always add ~/.coral directory
			 */
			std::string home(chome);
			home += "/.coral";
			new_coral_path.push_back(home);
				
		}
		setCoralPath(new_coral_path);
	}

	void initPython() {
		Py_Initialize();
	}

	NAMESPACE_END();

	void print_backtrace() throw () {
		print_c_backtrace();
		write(STDERR_FILENO,
		      "Python backtrace:\n",
		      18);
		print_python_backtrace();
	}


	int init_coral_framework() throw () {
		static int has_loaded = 0;

		if (has_loaded)
			return 1;
		static int i = 0;
		++i;
		if (i > 1) {
			dlog << DEBUGLEVEL_DEBUG << "You are recursing in init_coral_framework!"
				  << std::endl;
			coral_assert(0);
			return 1;
		}
			

		signal(SIGSEGV, sighandler_SEGV);

		try {
			INIT_MUTEX_FUNC(&mutex_languages);
			INIT_MUTEX_FUNC(&mutex_modelelements);
			INIT_MUTEX_FUNC(&mutex_uuid);
#ifndef HAVE_LOCALTIME_R			
			INIT_MUTEX_FUNC(&mutex_localtime_r);
#endif
			initPython();
			fixDebugLevel();
			fixInstallationPath();
			fixCoralPath();
			initGetUniqueID();
			unicodestring::initUnicodeCharacterConversions();
			CORE::init_language();
			XMI::init_language();
			SMD01::init_language();

			/*
			 * Add XMLNS RMAP entries for the languages
			 * That are already bootstrapped into the kernel
			 */
			const std::string ip(installation_path);
			std::string score01 = ip + "/coral/core/CORE01.xmi";
			addXMLNSRmap("http://www.abo.fi/CORE/0.1",
				     score01, "CORE", 0, 1);
			std::string sxmi12 = ip + "/coral/core/XMI12.xmi";
			addXMLNSRmap("http://schema.omg.org/spec/XMI/1.2",
				     sxmi12, "XMI", 1, 2);
			std::string ssmd01 = ip + "/coral/lang/SMD01/def/SMD01.xmi";
			addXMLNSRmap("http://www.abo.fi/SMD/0.1",
				     ssmd01, "SMD", 0, 1);

			/*
			 * We can't add this to core since
			 * it requires XMI which requires core...
			 */
			kind_t k;
			k |= kind_t::KIND_COMPOSITION;
			coral::Property* p;
			p = new Property("xmi_extension",
					 XMI::Extension,
					 NULL,
					 0, -1,
					 k);
			autounref<ref_counted> _t(p);
			CORE::AnyElement->addPropertyRecursive(p);

			/*
			 *
			 */

			std::string s(installation_path);
			coral::Element* mm_smd = coral::io::loadModel((s + "/coral/lang/SMD01/def/SMD01.xmi").c_str());
			coral::SMD01::smd2lang(mm_smd);

		} catch (...) {
			return 0;
		}

		has_loaded = 1;
		return 1;
	}

}
