/** @file filter.cpp
 *
 *	Interface declaration of module performing some filtration tasks.
 *
 *	See filter.h for more info.
 *
 *	(C) Ceslav Przywara 2008, MFF UK Prague
 *
 */

#include <fstream>
// Utility std::fill needed.
#include <utility>

// We are interested in value of INSIGNIFICANT_POSITION_MARK.
#include "config.h"
// Self header.
#include "filter.h"

namespace ace {

/** Returns true, if value to match (VTM) matches the rule (R).
 *	Note: If sizes of strings are different, only the corresponding substring
 *	of the longer one is considered.
 *	VTM matches R, if and only if all characters from of R except the
 *	insignificant one matches its counterpart in V.
 *	@param rule
 *	@param value_to_match
 *	@return True, if value_to_match matches rule, false otherwise.
 */
bool match_single_rule(const std::string& rule, const std::string& value_to_match) {
	for ( std::string::size_type i = 0; (i < value_to_match.size()) && (i < rule.size()); ++i ) {
		if ( (rule[i] != constants::insignificant_position_mark) && (rule[i] != value_to_match[i]) ) {
			return false;
		}
	}
	// Hola!:)
	return true;
}


//// Filter ////

/*	Interface item implementation - for info see description in header file.
 */
void Filter::Stats::reset(void) {
	unmatched = 0;
	// Reset doesn't mean clear!
	std::fill(matched.begin(), matched.end(), 0);
}


//// UniRulesFilter ////

/*	Interface item implementation - for info see description in header file.
 */
void UniRulesFilter::add_rule(unirule_t& rule) {
	_rules.push_back(rule);
	_stats_so_far.matched.push_back(0);
	_recent_stats.matched.push_back(0);
}

/*	Interface item implementation - for info see description in header file.
 */
bool UniRulesFilter::match(const unirule_t& value, bool include_in_stats) {

	for ( rules_t::const_iterator i_rule = _rules.begin(); i_rule != _rules.end(); ++i_rule ) {
		// Loop through every rule and compare to given value.
		if ( match_single_rule(*i_rule, value) ) {
			if ( include_in_stats ) {
				++(_stats_so_far.matched[i_rule - _rules.begin()]);
				++(_recent_stats.matched[i_rule - _rules.begin()]);
			}
			return true;
		}
	}
	if ( include_in_stats ) {
		++_stats_so_far.unmatched;
		++_recent_stats.unmatched;
	}
	return false;
}

/*	Interface item implementation - for info see description in header file.
 */
bool UniRulesFilter::read_rules(const char *rules_filename) {
		
	std::ifstream rules_list(rules_filename);
	
	if ( !rules_list ) {
		return false;
	}

	unirule_t rule;

	while ( rules_list ) {
		// Get next line == get next rule.
		std::getline(rules_list, rule);

		if ( rule.empty() ) {
			continue;
		}

		add_rule(rule);
	}

	return true;
}


//// MultiRulesFilter ////
	
/*	Interface item implementation - for info see description in header file.
 */
void MultiRulesFilter::add_rule(multirule_t& rule) {
	_rules.push_back(rule);
	_stats_so_far.matched.push_back(0);
	_recent_stats.matched.push_back(0);
}

/*	Interface item implementation - for info see description in header file.
 */
bool MultiRulesFilter::match(const multirule_t& value, bool include_in_stats) {

	for ( rules_t::const_iterator i_rule = _rules.begin(); i_rule != _rules.end(); ++i_rule ) {
		// Loop through sets of rules...
		if ( i_rule->size() != value.size() ) {
			// Different sized rules cannot match in any way.
			continue;
		}
		unsigned i = 0; // Needed after loop also.
		for ( ; i < i_rule->size(); ++i ) {
			// Loop through every member of the rule and match
			if ( !match_single_rule((*i_rule)[i], value[i]) ) {
				// This rule doesn't match. Try next one.
				break;
			}
		}
		if ( i == i_rule->size() ) {
			// Ok, matched!
			if ( include_in_stats ) {
				++(_stats_so_far.matched[i_rule - _rules.begin()]);
				++(_recent_stats.matched[i_rule - _rules.begin()]);
			}
			return true;
		}
	}

	// No matching rule found.
	if ( include_in_stats ) {
		++_stats_so_far.unmatched;
		++_recent_stats.unmatched;
	}
	return false;
}

/*	Interface item implementation - for info see description in header file.
 */
bool MultiRulesFilter::read_rules(const char *rules_filename, size_t only_of_size) {
	
	std::ifstream rules_list(rules_filename);
	
	if ( !rules_list ) {
		return false;
	}

	multirule_t rule;
	std::string str;

	while ( rules_list ) {
		// Get next line...
		std::getline(rules_list, str);

		if ( str.empty() ) {
			continue;
		}

		std::string::size_type start = 0, end = str.find(' ');

		while ( end != std::string::npos ) {
			rule.push_back(str.substr(start, end - start));
			start = end + 1;
			end = str.find(' ', start);
		}
		// Final piece...
		if ( start < str.size() ) {
			rule.push_back(str.substr(start));
		}
		if ( (only_of_size == 0) || (rule.size() == only_of_size) ) {
			// (0 -> store all rules)
			add_rule(rule);
		}
		
		// Prepare for next loop!
		rule.clear();
	}

	return true;

}

} // namespace ace
