#include <ipaaca.h>
#include <typeinfo>

using namespace ipaaca;

const char RECV_CATEGORY[] = "WORD";
const char SEND_CATEGORY[] = "TEXT";

class TextSender {
	protected:
		OutputBuffer::ptr _ob;
		InputBuffer::ptr  _ib;
	public:
		TextSender();
		void outbuffer_handle_iu_event(IUInterface::ptr iu, IUEventType event_type, bool local);
		void inbuffer_handle_iu_event(IUInterface::ptr iu, IUEventType event_type, bool local);
		IUInterface::ptr find_last_iu();
		void publish_text_to_print(const std::string& text, const std::string& parent_iu_uid="");
};

TextSender::TextSender() {
	_ob = OutputBuffer::create("TextSenderOut");
	_ob->register_handler(boost::bind(&TextSender::outbuffer_handle_iu_event, this, _1, _2, _3));
	_ib = InputBuffer::create("TextSenderIn", RECV_CATEGORY);
	_ib->register_handler(boost::bind(&TextSender::inbuffer_handle_iu_event, this, _1, _2, _3));
}

void TextSender::outbuffer_handle_iu_event(IUInterface::ptr iu, IUEventType event_type, bool local)
{
	std::cout << "(own IU event " << iu_event_type_to_str(event_type) << " " << iu->uid() << ")" << std::endl;
	if (event_type == IU_UPDATED) {
		std::set<std::string> parent_uids = iu->get_links("GRIN");
		if (parent_uids.size() > 0) {
			std::string parent_uid = *(parent_uids.begin());
			std::cout << "updating parent ..." << std::endl;
			std::set<std::string> next_uids = iu->get_links("SUCCESSOR");
			if (next_uids.size() > 0) {
				std::string next_uid = *(next_uids.begin());
				IUInterface::ptr next_iu = _ob->get(next_uid);
				std::set<std::string> next_letter_grin_links = next_iu->get_links("GRIN");
				if (next_letter_grin_links.count(parent_uid) == 0) {
					// next letter belongs to new word
					IUInterface::ptr parent_iu = _ib->get(parent_uid);
					parent_iu->payload()["STATE"] = "REALIZED";
				} else {
					IUInterface::ptr parent_iu = _ib->get(parent_uid);
					parent_iu->payload()["STATE"] = "STARTED";
				}
			} else {
				// there are no more letters, this is the end of the final word
				IUInterface::ptr parent_iu = _ib->get(parent_uid);
				parent_iu->payload()["STATE"] = "REALIZED";
			}
			std::cout << " ... done." << std::endl;
		}
	} else {
	}
}

void TextSender::inbuffer_handle_iu_event(IUInterface::ptr iu, IUEventType event_type, bool local)
{
	if (event_type == IU_LINKSUPDATED) {
		std::cout << "links updated" << std::endl;
	} else if (event_type == IU_ADDED) {
		std::string word = iu->payload()["WORD"];
		std::cout << "Received new word: " << word << std::endl;
		publish_text_to_print(word, iu->uid());
	} else if (event_type == IU_RETRACTED) {
		std::string retracted_uid = iu->uid();
	} else {
		std::cout << "(IU event " << iu_event_type_to_str(event_type) << " " << iu->uid() << ")" << std::endl;
	}
}

IUInterface::ptr TextSender::find_last_iu() {
	std::set<IUInterface::ptr> ius = _ob->get_ius();
	for (std::set<IUInterface::ptr>::iterator it = ius.begin(); it!=ius.end(); ++it) {
		if ((*it)->get_links("SUCCESSOR").size() == 0) return *it;
	}
	return IUInterface::ptr();
}

void TextSender::publish_text_to_print(const std::string& text, const std::string& parent_iu_uid) {
	IUInterface::ptr previous_iu = find_last_iu();
	if (previous_iu) {
		// insert a blank if we already have words in the buffer
		IU::ptr iu = IU::create( SEND_CATEGORY );
		iu->payload()["CONTENT"] = " ";
		_ob->add(iu);
		previous_iu->add_link( "SUCCESSOR", iu->uid() );
		iu->add_link( "PREDECESSOR", previous_iu->uid() );
		if (parent_iu_uid != "") iu->add_link( "GRIN", parent_iu_uid );
		previous_iu = iu;
	}
	for (int i=0; i<text.size(); ++i) {
		IU::ptr iu = IU::create( SEND_CATEGORY );
		iu->payload()["CONTENT"] = std::string(1, text.at(i));
		_ob->add(iu);
		if (previous_iu) {
			previous_iu->add_link( "SUCCESSOR", iu->uid() );
			iu->add_link( "PREDECESSOR", previous_iu->uid() );
			if (parent_iu_uid != "") iu->add_link( "GRIN", parent_iu_uid );
		}
		if (previous_iu) std::cout << "previous IU: " << *previous_iu << std::endl;
		previous_iu = iu;
	}
}
	
int main() {
	TextSender sender;
	sleep(1);
	sender.publish_text_to_print("(INIT)");
	std::cout << "Press Ctrl-C to cancel..." << std::endl;
	while (true) sleep(1);
}