#include <libwccl/values/match.h>
#include <libwccl/ops/match/actions/markmatch.h>

#include <sstream>


namespace Wccl {

void MarkMatch::execute(const ActionExecContext& context) const
{
	SentenceContext& sc = context.sentence_context();
	boost::shared_ptr<Corpus2::AnnotatedSentence> as;
	as = boost::dynamic_pointer_cast<Corpus2::AnnotatedSentence>(sc.get_sentence_ptr());
	if (!as) {
		throw InvalidArgument("context", "Operator needs an annotated sentence.");
	}

	boost::shared_ptr<const Match> match_from = match_from_->apply(context);
	boost::shared_ptr<const Match> match_to =
		(match_from_.get() == match_to_.get()) ? match_from : match_to_->apply(context);
	boost::shared_ptr<const Match> head_match =
		(match_from_.get() == head_match_.get()) ? match_from : head_match_->apply(context);

	int abs_left = match_from->first_token(as).get_value();
	if (abs_left < 0) {
		throw WcclError("Received starting match that points outside sentence.");
	}

	int abs_right = match_to->last_token(as).get_value();
	if (abs_right >= sc.size()) {
		throw WcclError("Received ending match that points outside sentence.");
	}
	if (abs_left > abs_right) {
		throw WcclError("Received starting match points after the received ending match.");
	}

	int abs_head = head_match->first_token(as).get_value();
	if (abs_head < abs_left || abs_head > abs_right) {
		throw WcclError("Received head match points outside range defined by start and end matches.");
	}

	if (!as->has_channel(chan_name_)) {
		as->create_channel(chan_name_);
	}
	Corpus2::AnnotationChannel& channel = as->get_channel(chan_name_);

	int segment_idx = channel.get_new_segment_index();

	for (int i = abs_left; i <= abs_right; ++i) {
		if (channel.get_segment_at(i) > 0) {
			throw WcclError("Mark action would overwrite existing annotation");
		}
	}
	for (int i = abs_left; i <= abs_right; ++i) {
		channel.set_segment_at(i, segment_idx);
		channel.set_head_at(i, false);
	}
	channel.set_head_at(abs_head, true);
}

std::string MarkMatch::to_string(const Corpus2::Tagset& tagset) const
{
	std::ostringstream os;
	os << name() << "("
			<< match_from_->to_string(tagset) << ", ";
	if (match_from_.get() != match_to_.get()) {
		os << match_to_->to_string(tagset) << ", ";
	}
	os << "\"" << chan_name_ << "\")";
	return os.str();
}

std::ostream& MarkMatch::write_to(std::ostream& os) const
{
	os << name() << "("
			<< *match_from_ << ", ";
	if (match_from_.get() != match_to_.get()) {
		os << *match_to_ << ", ";
	}
	os << "\"" << chan_name_ << "\")";
	return os;
}

} /* end ns Wccl */