/***************************************************************************
 *   Copyright (C) 2005 by Andreas Pokorny                                 *
 *   andreas.pokorny@biozentrum.uni-wuerzburg.de                           *
 *                                                                         *
 *   This file is part of profdist and cbcanalyzer                         *
 *                                                                         *
 *   Both profdist and cbcanalyzer are 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 2 of the * 
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   Profdist and cbcanalyzer are 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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifndef CBCANALYZER_RNA_FORESTER_PARSER_H_INCLUDED
#define CBCANALYZER_RNA_FORESTER_PARSER_H_INCLUDED

//#define BOOST_SPIRIT_DEBUG
//#define BOOST_SPIRIT_DEBUG_FLAGS BOOST_SPIRIT_DEBUG_FLAGS_NODES

#include <list>
#include <stdexcept>
#include <iostream>
#include <string>
#include <cassert>
#include <sstream>
#include <boost/cstdlib.hpp>
#include <boost/spirit/phoenix.hpp>
#include <boost/spirit/core.hpp>
#include <boost/spirit/symbols.hpp>
#include <boost/spirit/attribute.hpp>
#include <boost/spirit/dynamic.hpp>
#include <boost/spirit/core/primitives/primitives.hpp>
#include <boost/spirit/utility/grammar_def.hpp>
#include <boost/spirit/iterator/file_iterator.hpp>
#include <boost/spirit/iterator/position_iterator.hpp>
#include <boost/spirit/core/composite/epsilon.hpp>
#include <boost/spirit/actor/push_back_actor.hpp>
#include <boost/spirit/actor/clear_actor.hpp>
#include <boost/spirit/actor/increment_actor.hpp>
#include <boost/lambda/bind.hpp>

#include "parser.h"
#include "spirit_helper.h"



/**
 * Simple ct_file grammar, this grammar generates a list of sequence info 
 * items, 
 */
struct forester_grammar 
:   public boost::spirit::grammar<forester_grammar>
{
  std::list<file_sequence> & entries;
  forester_grammar( std::list<file_sequence> & entries ) : entries(entries) {}
  
  /**
   * grammar definition
   */
  template<typename ScannerT>
  struct definition
  {
    typedef boost::spirit::rule<ScannerT> rule_t;

    rule_t forester, names, score, fold, seq_string;

    file_sequence::string_range current_range; ///< temporary storage for list entries 
    file_sequence current_sequence; ///< Sequence entry which gets parsed at the moment,
    std::list<file_sequence>::iterator cur_it;

    void add_range_to_fold()
    {
//      std::cout << "Adding Range to fold data: " << std::string(cur_it->id.first, cur_it->id.second) << std::endl;
      cur_it++->char_fold_data.push_back( current_range );
    }

    void add_range_to_sequence()
    {
      cur_it->sequence_length += std::distance( current_range.first, current_range.second );
//      std::cout << "LENGTH=" << cur_it->sequence_length;
      cur_it++->sequence_data.push_back( current_range );
    }


    definition(forester_grammar const& self)
    {
      using boost::spirit::assign_a;
      using boost::spirit::increment_a;
      using boost::spirit::push_back_a;
      using boost::spirit::ch_p;
      using boost::spirit::real_p;
      using boost::spirit::blank_p;
      using boost::spirit::chset;
      using boost::spirit::eps_p;
      using boost::spirit::alpha_p;
      using boost::spirit::str_p;
      using boost::lambda::bind;

      chset<> ws_lbr("\n\r\t ");
      
      forester = 
        names 
        >> !score
        >> +(
            fold
            >> seq_string
            >> *ws_lbr
            )
        ;

      names =
        '>' 
        >> *blank_p
        >> (+(~chset<>("\n\r")))
        [ assign_a(current_sequence.id) ]
        [ boost::spirit::push_back_a(self.entries, current_sequence) ]
        >> *ws_lbr
        >> *(
            str_p("and")
            >> *ws_lbr
            >> '>'
            >> *ws_lbr
            >> (+( ~chset<>("\n\r") ))
            [ assign_a(current_sequence.id) ]
            [ boost::spirit::push_back_a(self.entries, current_sequence) ]
            >> *ws_lbr
         )
        ;
      
      score =  // optinal score info ( if line does not start with (.)- it should be the single score line:
        *blank_p
        >> ~chset<>(".()-")
        >> *~chset<>("\n\r")
        >> *lf_p
        ;

      fold = 
        eps_p 
        [ begin_a(cur_it, self.entries) ]
        >> *ws_lbr
        >> +(
            (+chset<>(".()-"))
            [ assign_a(current_range) ]
            [ bind( &forester_grammar::definition<ScannerT>::add_range_to_fold, this ) ]
         //   [ increment_a( var(cur_it) )]
            >> *ws_lbr
         )
        ;

      seq_string =
        eps_p
        [ begin_a(cur_it, self.entries) ]
        >> *ws_lbr
        >> +(
            (+chset<>("AaBbCcDdGgHhKkMmNnOoUuSsTtVvWwYy-"))
            [ assign_a(current_range) ]
            [ bind( &forester_grammar::definition<ScannerT>::add_range_to_sequence, this ) ]
         //   [ increment_a( var(cur_it) )]
            >> *ws_lbr
         )
        ;
        
   
#ifdef BOOST_SPIRIT_DEBUG
      BOOST_SPIRIT_DEBUG_RULE( seq_string );
      BOOST_SPIRIT_DEBUG_RULE( fold );
      BOOST_SPIRIT_DEBUG_RULE( names );
      BOOST_SPIRIT_DEBUG_RULE( score );
      BOOST_SPIRIT_DEBUG_RULE( forester );
#endif
    }

    rule_t const& start() const
    {
      return forester;
    }
  };
};



#endif 

