/*
 * 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 <iostream>
#include <fstream>
#include <sstream>
#include <chrono>
#include <ctime>
#include <cstring>
#include <cstdlib>

#include "belr/grammarbuilder.h"
#include "belr/abnf.h"
#include "bctoolbox/logging.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;
};
