/* Tuplr: A programming language
 * Copyright (C) 2008-2009 David Robillard <dave@drobilla.net>
 *
 * Tuplr is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Tuplr 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 Affero General
 * Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Tuplr.  If not, see <http://www.gnu.org/licenses/>.
 */

/** @file
 * @brief Main program
 */

#include <cerrno>
#include <cstring>
#include <fstream>
#include "tuplr.hpp"

using namespace std;

GC Object::pool(8 * 1024 * 1024);

int
print_usage(char* name, bool error)
{
	ostream& os = error ? cerr : cout;
	os << "Usage: " << name << " [OPTION]... [FILE]..."  << endl;
	os << "Evaluate and/or compile Tuplr code"           << endl;
	os << endl;
	os << "  -h               Display this help and exit"        << endl;
	os << "  -r               Enter REPL after evaluating files" << endl;
	os << "  -p               Pretty-print input only"           << endl;
	os << "  -g               Debug (disable optimisation)"      << endl;
	os << "  -d               Dump assembly output"              << endl;
	os << "  -e EXPRESSION    Evaluate EXPRESSION"               << endl;
	os << "  -o FILE          Write output to FILE"              << endl;
	return error ? 1 : 0;
}

int
main(int argc, char** argv)
{
	// Read command line arguments
	map<string,string> args;
	list<string>       files;
	for (int i = 1; i < argc; ++i) {
		if (!strncmp(argv[i], "-h", 3)) {
			return print_usage(argv[0], false);
		} else if (argv[i][0] != '-') {
			files.push_back(argv[i]);
		} else if (!strncmp(argv[i], "-r", 3)
				|| !strncmp(argv[i], "-p", 3)
				|| !strncmp(argv[i], "-g", 3)
				|| !strncmp(argv[i], "-d", 3)) {
			args.insert(make_pair(argv[i], ""));
		} else if (i == argc-1 || argv[i+1][0] == '-') {
			return print_usage(argv[0], true);
		} else {
			args.insert(make_pair(argv[i], argv[i+1]));
			++i;
		}
	}

	PEnv penv;
	TEnv tenv(penv);
	initLang(penv, tenv);

	Engine* engine = NULL;

	map<string,string>::const_iterator a = args.find("-b");
	const string backend_name = (a != args.end() ? a->second : "llvm");

	if (backend_name == "llvm")
		engine = tuplr_new_llvm_engine();

	if (!engine) {
		std::cerr << "Unable to open backend " << backend_name << std::endl;
		return 1;
	}

	CEnv* cenv = new CEnv(penv, tenv, engine);
	cenv->args = args;
	cenv->push();

	Object::pool.lock();

	int ret = 0;

	a = args.find("-o");
	const string output = (a != args.end()) ? a->second : "";

	if (args.find("-p") != args.end()) {
		ifstream is(files.front().c_str());
		if (is.good()) {
			Cursor loc;
			AST* exp = readExpression(loc, is);
			AST* ast = penv.parse(exp);
			pprint(cout, ast);
		}
		return 0;
	}

	a = args.find("-e");
	if (a != args.end()) {
		istringstream is(a->second);
		ret = eval(*cenv, "(command line)", is);
	}

	for (list<string>::iterator f = files.begin(); f != files.end(); ++f) {
		ifstream is(f->c_str());
		if (is.good()) {
			ret = ret | eval(*cenv, *f, is);
		} else {
			cerr << argv[0] << ": " << *f << ": " << strerror(errno) << endl;
			++ret;
		}
		is.close();
	}

	if (args.find("-r") != args.end() || (files.empty() && args.find("-e") == args.end()))
		ret = repl(*cenv);

	if (output != "") {
		ofstream os(output.c_str());
		if (os.good()) {
			cenv->engine()->writeModule(*cenv, os);
		} else {
			cerr << argv[0] << ": " << a->second << ": " << strerror(errno) << endl;
			++ret;
		}
		os.close();
	}

	delete cenv;
	delete engine;

	return ret;
}