/** @file buffer.h
 *
 *	Declaration of words buffer interface.
 *
 *	Advanced buffering of words is required by context tracing functionality.
 *
 *	(C) Ceslav Przywara 2008, MFF UK Prague
 *
 */

#ifndef _BUFFER_H
#define	_BUFFER_H

#include <list>
// We use pair here.
#include <utility>

// Some base types
#include "config.h"
// Parser used
#include "parser.h"
// Count stats
#include "stats.h"

namespace ace {

/** Words range is a range within the buffer, so its based on word buffer
 *	container type (see word.h). The range stays valid as long as no pop/push
 *	operations are perfomed on the buffer. The range conforms to container
 *	ranges behavior (behaves just like begin() and end() iterators).
 */
typedef std::pair<words_store_t::const_iterator, words_store_t::const_iterator> words_range_t;

/** Class implements words buffer.
 *	Words buffer is used for retrieval of data from datafiles (by using Parser,
 *	see parser.h). Buffer main purpose is to push in as much data as needed for
 *	tracing of N-grams context and to drop this data just in the moment when
 *	they become unneeded.
 */
class Buffer {

	/** Index of sentence beginnning (determines first word of sentence within
	 *	buffer) and length of sentence.
	 */
	typedef std::pair<size_t, size_t> sentence_index_t;
	/** Buffer may hold multiple sentences. They're indices are stored in list
	 *	because we need insert/delete that keeps iterators valid.
	 */
	typedef std::list<sentence_index_t> sentences_indices_t;

	/** Underlying parser.
	 */
	Parser& _parser;
	/** Container for words.
	 */
	words_store_t _words;
	/** Sentences bounds.
	 */
	sentences_indices_t _sentences;
	/** Current sentence (in every moment only one sentence is current!).
	 *	There is no current sentence if buffer is empty.
	 */
	sentences_indices_t::iterator _current;
	/** Context window is needed to provide efficient pop/push operations.
	 */
	const ContextWindow _context_window;
	/** Stats of pushed-in data.
	 */
	DataFileStats _stats;
	/** Returns index of given word (pointed by iterator) within the buffer.
	 *	@param iter Iterator to word within buffer (must be valid!).
	 *	@return Index of given word (pointed by iterator) within the buffer.
	 */
	inline size_t _index_of(words_store_t::const_iterator iter) const { return iter - _words.begin(); }
	/** Inits buffer and pushes in some data (amount is determined by context
	 *	window settings).
	 */
	void _init(void);
	/** Pops top sentence from queue (doesn't check if buffer is non-empty!).
	 *	@return Number of words in popped sentence.
	 */
	size_t _pop(void);
	/** Push next sentence(s) into the buffer, params determine(s) number of
	 *	sentence(s) pushed - ther're forwarded to parser (see Parser::next()).
	 *	@param needed Amount of sentences (words) wanted to be pushed in.
	 *	@param context_window_mode Determines, if the `needed` param means
	 *		words or sentences.
	 */
	void _push(size_t needed = 1, ContextWindow::Mode context_window_mode = ContextWindow::SENTENCE_MODE);

public:
	/** Creates buffer, which pushes some data in (from parser).
	 *	@param parser Parser to be used as data source.
	 *	@param context_window Used to keep buffer size big enough for context
	 *		tracing.
	 */
	Buffer(Parser& parser, ContextWindow context_window): _parser(parser), _words(), _sentences(), _current(), _context_window(context_window), _stats() {
		_init();		
	}
	/** @return Words range corresponding to whole buffer (SENTENCE MODE).
	 */
	inline words_range_t context_range(void) const { return words_range_t(_words.begin(), _words.end()); }
	/** @param head Head member of N-gram.
	 *	@return Words range coresponding to context window and given (N-gram)
	 *		head (WORD MODE).
	 */
	inline words_range_t context_range(words_store_t::const_iterator head) const { return context_range(head, head); }
	/** @param left Left-most N-gram member.
	 *	@param right Left-most N-gram member.
	 *	@return Words range coresponding to context window and given boundary
	 *	N-gram members.
	 */
	words_range_t context_range(words_store_t::const_iterator left, words_store_t::const_iterator right) const;
	/** @return True, if buffer is empty.
	 */
	inline bool empty(void) const { return _words.empty(); }
	/** Advances to the next sentence (move current sentence pointer, pushes
	 *	in some data if needed, pops some data if possible).
	 *	@return True, if current sentence pointer move performed successfully.
	 */
	bool next(void);
	/**	@return Current sentence range.
	 */
	words_range_t current(void) const;
	/** @return Copy of stats for data pushed in (so far).
	 */
	inline DataFileStats stats(void) const { return _stats; }
};

} // namespace ace

#endif
