//#define BOOST_SPIRIT_DEBUG 
#include <boost/spirit/core.hpp>
#include <boost/spirit/tree/ast.hpp>
#include <boost/spirit/tree/tree_to_xml.hpp>
#include <string>
#include <iostream>
#include <map>
#include <boost/any.hpp>
#include <stdlib.h>
#include <typeinfo>
#include "wp_parse.hxx"
#include "WPManager.hxx"
using namespace std;
using namespace boost::spirit;
using namespace boost;

typedef char const*         iterator_t;
typedef tree_match<iterator_t> parse_tree_match_t;
typedef parse_tree_match_t::tree_iterator iter_t;
any evaluate(parse_tree_match_t hit);
any eval_expression(iter_t const& i);

////////////////////////////////////////////////////////////////////////////
//
//  Our watchpoint grammar
//
////////////////////////////////////////////////////////////////////////////
struct wp_grammar : public grammar<wp_grammar>
{
    static const int watchpointID = 1;
    static const int moduleListID = 2;
    static const int expID = 3;
    static const int moduleID = 4;
	static const int moduleHelperID = 9;
	static const int stateVarID = 5;
    static const int rValID = 6;
    static const int compExpID = 7;
	static const int identifierID = 8;
	static const int compOpID = 10;
	static const int expSimpleID = 11;
	static const int numericID = 12;

    template <typename ScannerT>
    struct definition
    {
        definition(wp_grammar const& /*self*/)
        {
            //  Start grammar definition
			wp = infix_node_d[moduleList >> ';' >> exp];
			moduleList = ch_p('(') >> infix_node_d[identifier % ','] >> ch_p(')');
			identifier = lexeme_d[token_node_d[alpha_p >> *("_"|alnum_p|':')]];
			expSimple = (ch_p('!') >> expSimple) |
				  (ch_p('n') >> '(' >> identifier >> ',' >> identifier >> ')') |
				  ('(' >> compExp >> ')') | ('(' >> exp >> ')');
			exp = (expSimple >> (ch_p('&') | ch_p('|')) >> exp) |
				  expSimple;
			module = identifier >> !moduleHelper;
			moduleHelper = +(str_p(".last") | str_p(".next"));
			stateVar = module >> '.' >> identifier;
			rVal = stateVar | numeric; 
			compExp = stateVar >> compOp >> rVal;
			compOp = str_p(">") | str_p(">=") | str_p("==") | str_p("<") | str_p("<=") | str_p("!=");
			numeric = int_p;
			//  End grammar definition
			//BOOST_SPIRIT_DEBUG_RULE(exp);
			//BOOST_SPIRIT_DEBUG_RULE(expSimple);
        }

        rule<ScannerT, parser_context<>, parser_tag<watchpointID> >   wp;
        rule<ScannerT, parser_context<>, parser_tag<moduleListID> >   moduleList;
        rule<ScannerT, parser_context<>, parser_tag<expID> >          exp;
				rule<ScannerT, parser_context<>, parser_tag<expSimpleID> >    expSimple;
        rule<ScannerT, parser_context<>, parser_tag<moduleID> >       module;
        rule<ScannerT, parser_context<>, parser_tag<moduleHelperID> > moduleHelper;
        rule<ScannerT, parser_context<>, parser_tag<stateVarID> >     stateVar;
        rule<ScannerT, parser_context<>, parser_tag<rValID> >         rVal;
        rule<ScannerT, parser_context<>, parser_tag<compExpID> >      compExp;
        rule<ScannerT, parser_context<>, parser_tag<compOpID> >       compOp;
        rule<ScannerT, parser_context<>, parser_tag<identifierID> >   identifier;
				rule<ScannerT, parser_context<>, parser_tag<numericID> >      numeric;

        rule<ScannerT, parser_context<>, parser_tag<watchpointID> > const&
        start() const { return wp; }
    };
};

any evaluate(tree_parse_info<> info)
{
    return eval_expression(info.trees.begin());
}

BoolExp *decodeAny(any a) {
	if (BoolExp **e = any_cast<BoolExp*>(&a)) return *e;
	else if (AndExp **e = any_cast<AndExp*>(&a)) return *e;
	else if (OrExp **e = any_cast<OrExp*>(&a)) return *e;
	else if (NotExp **e = any_cast<NotExp*>(&a)) return *e;
	else if (CompExp **e = any_cast<CompExp*>(&a)) return *e;
	else if (NeighborExp **e = any_cast<NeighborExp*>(&a)) return *e;
	cout << "no match in decodeAny:" << a.type().name() << endl;
	return NULL;
}

any eval_expression(iter_t const& i) {
	//cout << i->value.id().to_long() << endl;
	//do construction of matcher object here
	switch (i->value.id().to_long()) {
		case wp_grammar::watchpointID:
			assert(i->children.size() == 2);
			return any(new Watchpoint(any_cast<CatomList*>(eval_expression(i->children.begin())),
								  decodeAny(eval_expression(i->children.begin()+1))));
		case wp_grammar::moduleListID: {
				CatomList *cl = new CatomList();
				for (unsigned j = 1; j < i->children.size() -1; j++) {
					string s(i->children[j].value.begin(),i->children[j].value.end());
					cl->addName(s.c_str());
				}
				return any(cl);
			}
		case wp_grammar::identifierID:
			return any(string(i->value.begin(), i->value.end()));	
		case wp_grammar::expSimpleID: {
			BoolExp *head;
			if (*i->children[0].value.begin() == '!')
	        {
				head = new NotExp(any_cast<BoolExp*>(eval_expression(i->children.begin()+1)));
			}
			else if (*i->children[0].value.begin() == 'n')
	        {
				head = new NeighborExp((any_cast<string>(eval_expression(i->children.begin()+2))).c_str(),
										    (any_cast<string>(eval_expression(i->children.begin()+4))).c_str());
			}
			else if (i->children[1].value.id() == wp_grammar::expID) {
				head = decodeAny(eval_expression(i->children.begin()+1));
			}
			else {
				head = any_cast<CompExp*>(eval_expression(i->children.begin()+1));
			}
			return any(head);
		}
		case wp_grammar::expID:
			if (i->children.size() == 3) {
				if (*i->children[1].value.begin() == '&')
		        {
					return any(new AndExp(decodeAny(eval_expression(i->children.begin())),
								  		  decodeAny(eval_expression(i->children.begin()+2)))); 
				}
				else {
					return any(new OrExp(decodeAny(eval_expression(i->children.begin())),
								  		  decodeAny(eval_expression(i->children.begin()+2))));
				}
			}
			else {
				return eval_expression(i->children.begin());
			}
		case wp_grammar::moduleID: {
				string name(i->children[0].value.begin(),i->children[0].value.end());
				CatomRef *cr = new CatomRef(name.c_str());
				cr->temporalOffset =  any_cast<int>(eval_expression(i->children.begin()+1));
				return any(cr);
			}
		case wp_grammar::moduleHelperID: {
				int to = 0;
				//check moduleHelper's value
				string s(i->value.begin(),i->value.end());
				if (s == ".next") to++;
				else if (s == ".last") to--;
				//iterate through children
				for (unsigned j = 0; j < i->children.size(); j++) {
					string s(i->children[j].value.begin(),i->children[j].value.end());
					if (s == ".next") to++;
					else to--;
				}
				return any(to);
			}
		case wp_grammar::stateVarID: 
				if (i->children[0].value.id() == wp_grammar::identifierID) 
					return any(new CatomVar(new CatomRef((any_cast<string>(eval_expression(i->children.begin()))).c_str()),
								            (any_cast<string>(eval_expression(i->children.begin()+2))).c_str()));
				else 
					return any(new CatomVar(any_cast<CatomRef*>(eval_expression(i->children.begin())),
								           (any_cast<string>(eval_expression(i->children.begin()+2))).c_str()));
		case wp_grammar::rValID:
			return eval_expression(i->children.begin());
		case wp_grammar::compExpID:
			if (i->children[2].value.id().to_long() == (long)wp_grammar::stateVarID)
				return any(new CompExp(any_cast<CatomVar*>(eval_expression(i->children.begin())),
									  (any_cast<string>(eval_expression(i->children.begin()+1))).c_str(),
									   any_cast<CatomVar*>(eval_expression(i->children.begin()+2))));
			else
				return any(new CompExp(any_cast<CatomVar*>(eval_expression(i->children.begin())),
									  (any_cast<string>(eval_expression(i->children.begin()+1))).c_str(),
									   any_cast<Number*>(eval_expression(i->children.begin()+2))));
		case wp_grammar::compOpID:
			return any(string(i->value.begin(), i->value.end()));	
		case wp_grammar::numericID: {
			string s(i->value.begin(), i->value.end());
			return any(new Number(atoi(s.c_str())));
		}
		default:
			cout << "default: empty" << endl;
			return any();
	}
}

Watchpoint *parseWP(string input) {
	wp_grammar wp;
	//input = "(a,d,c); n(a,b) & n(b,c) | !(d.argh != b.foo) & (b.last.next.foo > 12)";
	cout << "Parser input:" << input << endl;
	tree_parse_info<> info = ast_parse(input.c_str(), wp,space_p);	
	if (info.full)
    {
        // dump parse tree as XML
		    std::map<parser_id, std::string> rule_names;
		    rule_names[wp_grammar::watchpointID] = "watchpoint";
				rule_names[wp_grammar::moduleListID] = "list";
				rule_names[wp_grammar::expID] = "exp";
				rule_names[wp_grammar::expSimpleID] = "expSimple";
				rule_names[wp_grammar::moduleID] = "module";
				rule_names[wp_grammar::moduleHelperID] = "moduleHelper";
				rule_names[wp_grammar::stateVarID] = "stateVar";
				rule_names[wp_grammar::rValID] = "rVal";
				rule_names[wp_grammar::compExpID] = "compExp";
				rule_names[wp_grammar::compOpID] = "compOp";
				rule_names[wp_grammar::identifierID] = "identifier";
				rule_names[wp_grammar::numericID] = "numeric";
				//tree_to_xml(cout, info.trees, input.c_str(), rule_names);

        // print the result
        //cout << "parsing succeeded\n";
		return any_cast<Watchpoint*>(evaluate(info));
    }
    else
    {
        cout << "watchpoint parsing failed\n";
		return NULL;
    }
}

void CatomVar::propagate(string name,CatomSim *catom,WPManager *wm){
	if (name == catomref->name) {
		filled = true;
		value = wm->lookupSavedState(catom,var,catomref->temporalOffset);
	}
}

// int main() {
// 	string input = "(a,d,c); n(a,b) & n(b,c) | !(d.argh != b.foo) & (b.last.next.foo > 12)";
// 	parseWP(input);
// 	return 0;
// }
