/** @file buffer.cpp
 *
 *	Definition of words buffer interface.
 *
 *	See buffer.h for more info.
 *
 *	(C) Ceslav Przywara 2008, MFF UK Prague
 *
 */

// Self header
#include "buffer.h"

namespace ace {

/*	Interface item implementation - for info see description in header file.
 */
void Buffer::_init(void) {
	// Read at least one line from parser.
	_push();

	if ( _context_window.radius != 0 ) {
		// Push more data.
		_push(_context_window.radius, _context_window.mode);
	}
	
	// Store current sentence.
	_current = _sentences.begin();
}

/*	Interface item implementation - for info see description in header file.
 */
size_t Buffer::_pop(void) {
	// 
	size_t dropped_sentence_length = _sentences.front().second;
	// Erase words from buffer.
	_words.erase(_words.begin(), _words.begin() + dropped_sentence_length);
	// Erase sentence index (this will fail, if buffer is empty!)
	_sentences.pop_front();
	// Alter indices of remaining sentences.
	for ( sentences_indices_t::iterator iter = _sentences.begin(); iter != _sentences.end(); ++iter ) {
		iter->first -= dropped_sentence_length;
	}
	return dropped_sentence_length;
}

/*	Interface item implementation - for info see description in header file.
 */
void Buffer::_push(size_t needed, ContextWindow::Mode context_window_mode) {
	// Alter sentence bounds and count parsed words.
	size_t index = _words.size();
	// Push next.
	if ( _parser.next(needed, context_window_mode, _words) ) {
		// # of parsed words in parsed sentences.
		const Parser::parsed_counter_t& parsed = _parser.parsed();
		for ( size_t i = 0; i < parsed.size(); ++i ) {
			// Alter indices which determines sentences bounds.
			_sentences.push_back(sentence_index_t(index, parsed[i]));
			index += parsed[i];
			// Alter file stats.
			++_stats.sentences;
			_stats.words += parsed[i];
		}
	}
}

/*	Interface item implementation - for info see description in header file.
 */
words_range_t Buffer::context_range(words_store_t::const_iterator left, words_store_t::const_iterator right) const {
	// WORDS_MODE:
	size_t left_index = _index_of(left), right_index = _index_of(right);
	words_store_t::const_iterator first, second;
	if ( left_index >= _context_window.radius ) {
		first = _words.begin() + (left_index - _context_window.radius);
	} else {
		first = _words.begin();
	}
	if ( (_words.size() - (right_index + 1)) >= _context_window.radius ) {
		second = _words.end() - (_words.size() - (right_index + 1 + _context_window.radius));
	} else {
		second = _words.end();
	}
	return words_range_t(first, second);
}

/*	Interface item implementation - for info see description in header file.
 */
bool Buffer::next(void) {
	if ( empty() ) {
		// Can't do nothing.
		return false;
	}

	// PUSH (you can do it!).
	// Pushable?
	if ( !_parser.eof() ) {
		// Push needed?
		if ( _context_window.mode == ContextWindow::SENTENCE_MODE ) {
			// Simply. Just push next sentence in.
			_push();
		} else {
			// Go to next sentence (which would shortly become current).
			sentences_indices_t::iterator next_one = _current; ++next_one;
			// There must be at least one sentence after current one.
			// If not, it means that we have parsed the whole file, so
			// the _parser.eof() will trigger true and we won't get here :)
			// Get so many words as needed to have at least `radius` words after the next sentence.
			// next_one->first + next_one->second == number of words from buffer beginning to the end of next_one sentence.
			size_t reserve = (_words.size() - (next_one->first + next_one->second));
			size_t needed = (_context_window.radius > reserve) ? (_context_window.radius - reserve) : 0;
			if ( needed > 0 ) {
				_push(needed, ContextWindow::WORD_MODE);
			}
		}
	}

	// ADVANCE (do what you can!).	
	++_current;
	if ( _current == _sentences.end() ) {
		// We reach the end, my friend...
		return false;
	}	

	// POP (it out!).
	// Can pop?
	if ( _context_window.mode == ContextWindow::SENTENCE_MODE ) {
		// Simply. Just pop front sentence out.
		_pop();
	} else {
		// Get index of new front side buffer end (note that current has already been advanced!)
		if ( _index_of(current().first) > _context_window.radius ) {
			// Ok, pop may be possible (there's more words than required radius).
			size_t reserve = _index_of(current().first) - _context_window.radius;
			while ( reserve > _sentences.front().second ) {
				// While reserve is bigger then the first sentence size,
				// do pop!
				reserve -= _pop();
			}
		}
		// Nothing to pop elsewhere.
	}
	
	return true;
}

words_range_t Buffer::current(void) const {
	// Will blow out, if buffer is empty!
	return words_range_t(_words.begin() + _current->first, _words.begin() + _current->first + _current->second);
}

} // namespace ace
