/***************************************************************************
 *   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_MARNA_PARSER_H_INCLUDED
#define CBCANALYZER_MARNA_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 <algorithm>
#include <cassert>
#include <sstream>
#include <boost/cstdlib.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/spirit/utility/lists.hpp>
#include <boost/ref.hpp>
#include <boost/lambda/bind.hpp>

#include "parser.h"
#include "spirit_helper.h"
 
/**
 * Simple marna grammar, this grammar generates a list of sequence info 
 * items, 
 */
struct marna_grammar 
:   public boost::spirit::grammar<marna_grammar>
{
  std::list<file_sequence> & entries;
marna_grammar( std::list<file_sequence> & entries ) : entries(entries) {}
  
  /**
   * grammar definition
   */
  template<typename ScannerT>
  struct definition
  {
    typedef boost::spirit::rule<ScannerT> rule_t;

    rule_t marna, name_fold, name_seq ;

    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;
	
	// this method adds to existing sequence entries the fold data
	// which comes later in the marna file.
    void add_range_to_fold()
    {
      cur_it++->char_fold_data.push_back( current_range );
    }

    void prepare_string_data( std::list<file_sequence> const& entries ) 
    {
      if( cur_it != const_cast<std::list<file_sequence> &>( entries) .end()  )
        current_range = cur_it->id;
    }


    definition(marna_grammar const& self)
    {
      using boost::spirit::assign_a;
      using boost::spirit::push_back_a;
      using boost::spirit::increment_a;
      using boost::spirit::clear_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::spirit::list_p;
      using boost::lambda::bind;
      using boost::ref;

      chset<> ws_lbr("\n\r\t ");

      marna =
        list_p(
            name_seq
            , eps_p
            )
        >> eps_p
        [ begin_a(cur_it, self.entries) ]
        >> *ws_lbr
        >> !(
            ( str_p("ConSeq")|"Seq" )
            >> *blank_p
            >> *~ws_lbr
            )
        >> *ws_lbr
        >> list_p(
            name_fold
            , eps_p
            )
        >> *ws_lbr
        >> *(
            (str_p("ConStr")|"Str")
            >> *~chset<>("\n\r")
            >> *chset<>("\n\r")
            )
        ;

      name_seq =
        *ws_lbr 
//        >> ((+~ws_lbr) - str_p("ConSeq") )
        >> (+( ~chset<>("\n\r") - (+blank_p >>  +chset<>("AaBbCcDdGgHhKkMmNnOoUuSsTtVvWwYy-") >> *blank_p >> lf_p ) )  - (str_p("ConSeq")|"Seq") ) 
        [ assign_a( current_sequence.id )]
        >> +blank_p
        >> (+(
              chset<>("AaBbCcDdGgHhKkMmNnOoUuSsTtVvWwYy-")
              [increment_a(current_sequence.sequence_length)]
             ))
        [ assign_a(current_range) ]
        >> *blank_p
        >> lf_p
        [ push_back_a(current_sequence.sequence_data, current_range ) ] 
        [ boost::spirit::push_back_a(self.entries, current_sequence)]
        [ clear_a(current_sequence) ]
        >> *blank_p
        ;
      
      name_fold =
        *ws_lbr
        >> eps_p
        [ bind( &marna_grammar::definition<ScannerT>::prepare_string_data, this, boost::ref(self.entries) ) ]
        >> str_p(ref(current_range.first),ref(current_range.second))
        >> *blank_p
        >> (+chset<>(".()-"))
        [ assign_a(current_range) ]
        [ bind( &marna_grammar::definition<ScannerT>::add_range_to_fold, this ) ]
        >> *blank_p
        >> !lf_p
        >> *blank_p
        ;
        
   
#ifdef BOOST_SPIRIT_DEBUG
      BOOST_SPIRIT_DEBUG_RULE( marna );
      BOOST_SPIRIT_DEBUG_RULE( name_fold );
      BOOST_SPIRIT_DEBUG_RULE( name_seq );
#endif
    }

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



#endif 

