//************************************************************************//
//									  //
//  Copyright 2013 Bertram Kopf (bertram@ep1.rub.de)			  //
//  	      	   Julian Pychy (julian@ep1.rub.de)			  //
//          	   - Ruhr-Universität Bochum 				  //
//									  //
//  This file is part of Pawian.					  //
//									  //
//  Pawian 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.	 	      	  	   	  //
//									  //
//  Pawian 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 Pawian.  If not, see <http://www.gnu.org/licenses/>.	  //
//									  //
//************************************************************************//

#pragma once

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/foreach.hpp>

#include "DecayTree/DecayTree.hh"

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <vector>


namespace setupGrammar
{
  namespace fusion = boost::fusion;
  namespace phoenix = boost::phoenix;
  namespace qi = boost::spirit::qi;
  namespace ascii = boost::spirit::ascii;
  
  ///////////////////////////////////////////////////////////////////////////
  //  Our mini DECAY tree representation
  ///////////////////////////////////////////////////////////////////////////

  struct decay_tree;
}

// We need to tell fusion about our setup struct
// to make it a first-class fusion citizen

BOOST_FUSION_ADAPT_STRUCT(
    setupGrammar::decay_tree,
    (std::string, name)
    (std::vector<setupGrammar::decay_node>, children)
    (std::vector<std::string>, addParticle)
    (std::vector<std::string>, cloneParticle)
    (std::vector<std::string>, modParticle)
    (std::vector<std::string>, beamInput)
    (std::vector<std::string>, mcInput)
    (std::vector<std::string>, defineTuple)
    (std::vector<std::string>, fitVars)
    (std::vector<std::string>, initialProps)
)

namespace setupGrammar
{
  ///////////////////////////////////////////////////////////////////////////
  //  Print out the mini decay tree
  ///////////////////////////////////////////////////////////////////////////
  int const tabsize = 4;
  decayGraph::EdgeList edgeList;
  
  void tab(int indent)
  {
    for (int i = 0; i < indent; ++i)
      std::cout << ' ';
  }

  void vertexToEdgeList(const std::string& name)
  {
    edgeList.lastVertexNumber++;
    edgeList.particleNames.insert(std::pair<int, std::string>(edgeList.lastVertexNumber, name));
    if (edgeList.decays.size() > 0) {
      (*edgeList.lastVertex)->daughters.push_back(edgeList.lastVertexNumber);
    }
    edgeList.decays.push_back(new decayGraph::Decay(edgeList.lastVertexNumber));
    edgeList.lastVertex = edgeList.decays.rbegin();
  }

  void finalToEdgeList(const std::string& name)
  {
    edgeList.lastVertexNumber++;
    edgeList.particleNames.insert(std::pair<int, std::string>(edgeList.lastVertexNumber, name));
    (*edgeList.lastVertex)->daughters.push_back(edgeList.lastVertexNumber);
    while(edgeList.lastVertex != edgeList.decays.rend() && 
	  (*edgeList.lastVertex)->daughters.size() == 2 )
      edgeList.lastVertex++;
  }


  struct decay_tree_printer
  {
    decay_tree_printer(int indent = 0)
      : indent(indent)
    {
    }
      
    void operator()(decay_tree const& decay) const;
    
    int indent;
  };
  
  struct decay_node_printer : boost::static_visitor<> {
    decay_node_printer(int indent = 0)
      : indent(indent)
    {}
      
    void operator()(decay_tree const& decay) const {
      decay_tree_printer(indent+tabsize)(decay);
    }
    
    void operator()(std::string const& text) const {
      tab(indent+tabsize);
      std::cout << "final state particle " << text << std::endl;
      finalToEdgeList(text);
    }
    
    int indent;
  };
  
  void decay_tree_printer::operator()(decay_tree const& decay) const {
    tab(indent);
    std::cout << decay.name << " -> " << std::endl;
    vertexToEdgeList(decay.name);
    
    BOOST_FOREACH(decay_node const& node, decay.children) {
      boost::apply_visitor(decay_node_printer(indent), node);
    }
    
    tab(indent);

    std::vector<std::string>::const_iterator iter;
        
    iter = decay.addParticle.begin();
    while (iter != decay.addParticle.end()) {
      std::cout << "new particle: " << *iter << std::endl;
      ++iter;
    }

    iter = decay.cloneParticle.begin();
    while (iter != decay.cloneParticle.end()) {
      std::cout << "cloned particle: " << *iter << std::endl;
      ++iter;
    }
    
    iter = decay.modParticle.begin();
    while (iter != decay.modParticle.end()) {
      std::cout << "modified particle: " << *iter << std::endl;
      ++iter;
    }

    std::cout << std::endl;
  }

  ///////////////////////////////////////////////////////////////////////////
  //  Our mini DECAY grammar definition
  ///////////////////////////////////////////////////////////////////////////
  //[tutorial_decay1_grammar
  template <typename Iterator>
  struct setup_file_grammar : qi::grammar<Iterator, decay_tree(), 
					  ascii::space_type> {
    setup_file_grammar() : setup_file_grammar::base_type(decay) {
      using qi::lit;
      using qi::lexeme;
      using ascii::char_;
      using ascii::string;
      using namespace qi::labels;
      
      using phoenix::at_c;
      using phoenix::push_back;
      using boost::phoenix::ref;
      
      restOfLine = lexeme[*(char_ -'\n') [_val += _1]];
      comment = (char_('#') >> lexeme[*(char_ -'\n')]);
      
      particleName = lexeme[+(char_('!','z') -',') [_val += _1]];
      decNode = (decay | particleName);
      goesTo = char_('-') >> char_('>');
      
      decay = +( comment ||
		 "addParticle"     
		 >> restOfLine[push_back(at_c<2>(_val), _1)] || 
		 "cloneParticle"      
		 >> restOfLine[push_back(at_c<3>(_val), _1)] || 
		 "modParticle"        
		 >> restOfLine[push_back(at_c<4>(_val), _1)] || 
		 "beamInput"          
		 >> restOfLine[push_back(at_c<5>(_val), _1)] || 
		 "mcInput"            
		 >> restOfLine[push_back(at_c<6>(_val), _1)] || 
		 "defineTuple"        
		 >> restOfLine[push_back(at_c<7>(_val), _1)] || 
		 "fitVariables"       
		 >> restOfLine[push_back(at_c<8>(_val), _1)] || 
		 "initialProperties"  
		 >> restOfLine[push_back(at_c<9>(_val), _1)] || 
		 ( char_('{')
		   >> (decay[push_back(at_c<1>(_val), _1)] 
		       || particleName[at_c<0>(_val) = _1])
		   >> goesTo
		   >> decNode   [push_back(at_c<1>(_val), _1)]
		   >> ','
		   >> decNode   [push_back(at_c<1>(_val), _1)]
		   >> char_('}') )
		 );
    }
      
    qi::rule<Iterator, decay_tree(), ascii::space_type> decay;
    qi::rule<Iterator, decay_node(), ascii::space_type> decNode;
    qi::rule<Iterator, std::string(), ascii::space_type> goesTo;
    qi::rule<Iterator, std::string(), ascii::space_type> comment;
    qi::rule<Iterator, std::string(), ascii::space_type> restOfLine;
    qi::rule<Iterator, std::string(), ascii::space_type> particleName;
  };

}
