/*
 * Copyright (c) 2016-2019 Belledonne Communications SARL.
 *
 * This file is part of belr - a language recognition library for ABNF-defined grammars.
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 <chrono>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iostream>
#include <sstream>

#include "bctoolbox/logging.h"
#include "belr/abnf.h"
#include "belr/grammarbuilder.h"

using namespace belr;
using namespace std;

int main(int argc, char *argv[]) {
	const char *file = nullptr, *message_file = nullptr;
	int rules_first = 0;
	int i;
	int repeat_count = 1;
	if (argc < 2) {
		cerr << argv[0]
		     << " [--repeat <count>] [--debug] <grammar file to load> - test an abnf and instanciate the parser"
		     << endl;
		cerr << argv[0]
		     << " [--repeat <count>] [--debug] <grammar file to load> <input file to parse> <entry rule> [rule1] "
		        "[rule2]..."
		     << endl;
		cerr << argv[0]
		     << " The grammar file may be either an ABNF grammar text file, or a compiled grammar generated by "
		        "belr-compiler tool."
		     << endl;
		return -1;
	}
	for (i = 1; i < argc; ++i) {
		if (strcmp(argv[i], "--repeat") == 0) {
			++i;
			if (i < argc) {
				repeat_count = atoi(argv[i]);
			}
		} else if (strcmp(argv[i], "--debug") == 0) {
			bctbx_set_log_level(NULL, BCTBX_LOG_DEBUG);
		} else {
			file = argv[i];
			++i;
			if (i < argc) message_file = argv[i];
			break;
		}
	}

	rules_first = i + 1;

	ifstream ifs;
	ifs.open(file);
	if (!ifs.good()) {
		cerr << "Cannot open " << file << endl;
		return -1;
	}
	char marker[10] = {0};
	ifs.read(marker, sizeof(marker) - 1);
	ifs.close();
	shared_ptr<Grammar> grammar;

	auto t_start = std::chrono::high_resolution_clock::now();
	if (string(marker) == "#!belr") {
		grammar = make_shared<Grammar>(file);
		if (grammar->load(file) == -1) {
			cerr << "Fail to load compiled grammar " << file << endl;
			return -1;
		}
	} else {
		ABNFGrammarBuilder builder;
		grammar = make_shared<Grammar>(file);
		grammar->include(make_shared<CoreRules>());
		grammar = builder.createFromAbnfFile(file, grammar);
	}
	auto t_end = std::chrono::high_resolution_clock::now();
	cout << "Grammar loading completed in " << std::chrono::duration<double, std::milli>(t_end - t_start).count()
	     << " milliseconds" << endl;

	if (message_file) {
		ifstream istr(message_file);
		if (!istr.is_open()) {
			cerr << "Could not open " << message_file << endl;
			return -1;
		}
		stringstream str;
		str << istr.rdbuf();
		DebugParser parser(grammar);
		list<string> rules;
		for (int i = rules_first; i < argc; ++i) {
			rules.push_back(argv[i]);
		}
		parser.setObservedRules(rules);
		size_t parsed;
		shared_ptr<DebugElement> ret;
		auto t_start = std::chrono::high_resolution_clock::now();
		for (int r = 0; r < repeat_count; ++r) {
			ret = parser.parseInput(argv[rules_first], str.str(), &parsed);
		}
		auto t_end = std::chrono::high_resolution_clock::now();
		if (parsed < str.str().size()) {
			cerr << "Parsing ended prematuraly at pos " << parsed << endl;
		} else {
			cout << "Parsing done in "
			     << std::chrono::duration<double, std::milli>(t_end - t_start).count() / repeat_count << " milliseconds"
			     << endl;
		}
		if (ret) {
			ret->tostream(0, cout);
		} else {
			cerr << "Parsing failed." << endl;
			return -1;
		}
	}
	return 0;
};
