#include <apps/thread.h>
#include <cppcms/util.h>
#include <cppcms/url_dispatcher.h>
#include <cppcms/url_mapper.h>
#include <cppcms/session_interface.h>
#include <cppcms/cache_interface.h>



namespace data {

reply_form::reply_form()
{
	using cppcms::locale::translate;
	author.message(translate("Author"));
	comment.message(translate("Comment"));
	send.value(translate("Send"));
	add(author);
	add(comment);
	add(send);
	author.limits(1,64);
	comment.limits(1,256);
}


} // data

namespace {
	std::string text2html(std::string const &s)
	{
		std::string tmp=cppcms::util::escape(s);
		std::string res;
		res.reserve(tmp.size());
		for(unsigned i=0;i<tmp.size();i++) {
			if(tmp[i]=='\n') {
				res+="<br />";
			}
			res+=tmp[i];
		}
		return res;
	}
}


namespace apps {

thread_shared::thread_shared(cppcms::service &s) : apps::master(s)
{
}


bool thread_shared::prepare(data::thread_shared &c,int id)
{
	master::prepare(c);
	c.thread_id = id;
	

	cppdb::result r;
	r=sql<<"SELECT title FROM threads WHERE id=?" << id << cppdb::row;
	if(r.empty()) {
		response().make_error_response(404);
		return false;
	}
	
	r>> c.title;
	c.text2html = text2html;
	return true;

}

flat_thread::flat_thread(cppcms::service &s) : thread_shared(s)
{
	dispatcher().assign(".*",&flat_thread::prepare,this,0);
	mapper().assign("{1}");
}


void flat_thread::prepare(std::string sid)
{
	std::string key = "flat_thread_" + sid;
	
	if(cache().fetch_page(key))
		return;

	cache().add_trigger("thread_" + sid);
	
	data::flat_thread c;
	int id = atoi(sid.c_str());
	if(!thread_shared::prepare(c,id))
		return;
	cppdb::result r;
	r=sql<<	"SELECT id,author,content "
		"FROM messages WHERE thread_id=? "
		"ORDER BY id" << id;
	
	c.messages.reserve(10);
	for(int i=0;r.next();i++) {
		c.messages.resize(i+1);
		r>>c.messages[i].msg_id>>c.messages[i].author>>c.messages[i].content;
	}
	session()["view"]="flat";
	render("flat_thread",c);

	cache().store_page(key);
}

typedef std::map<int,std::map<int,data::msg> > msg_ord_t;

namespace {

void make_tree(data::tree_t &messages,std::map<int,std::map<int,data::msg> > &content,int start)
{
	std::pair<msg_ord_t::iterator,msg_ord_t::iterator>
		range=content.equal_range(start);
	for(msg_ord_t::iterator p=range.first;p!=range.second;++p) {
		for(std::map<int,data::msg>::iterator p2=p->second.begin(),e=p->second.end();p2!=e;++p2) {
			data::tree_thread::tree_msg &m=messages[p2->first];
			m.author=p2->second.author;
			m.content=p2->second.content;
			m.msg_id=p2->second.msg_id;
			make_tree(m.repl,content,p2->first);
		}
	}
	
}

} // anon

tree_thread::tree_thread(cppcms::service &s) : thread_shared(s)
{
	dispatcher().assign(".*",&tree_thread::prepare,this,0);
	mapper().assign("{1}");
}



void tree_thread::prepare(std::string sid)
{
	std::string key = "tree_thread_" + sid;
	
	if(cache().fetch_page(key))
		return;

	cache().add_trigger("thread_" + sid);

	int id = atoi(sid.c_str());
	data::tree_thread c;
	if(!thread_shared::prepare(c,id))
		return;
	cppdb::result r;
	r=sql<<	"SELECT reply_to,id,author,content "
		"FROM messages WHERE thread_id=? "
		"ORDER BY reply_to,id DESC" << id;
	msg_ord_t all;
	while(r.next()) {
		int msg_id,rpl_id;
		std::string author,comment;
		r>>rpl_id>>msg_id;
		data::msg &message=all[rpl_id][msg_id];
		r>>message.author>>message.content;
		message.msg_id=msg_id;
	}
	
	make_tree(c.messages,all,0);

	session()["view"]="tree";
	render("tree_thread",c);

	cache().store_page(key);
}

reply::reply(cppcms::service &srv) : thread_shared(srv)
{
	dispatcher().assign(".*",&reply::prepare,this,0);
	mapper().assign("{1}");
}

void reply::prepare(std::string smid)
{
	int mid;
	mid=atoi(smid.c_str());

	data::reply c;
	
	if(request().request_method()=="POST") {
		c.form.load(context());
		if(c.form.validate()) {
			cppdb::transaction tr(sql);
			cppdb::result r;
			r=sql<<"SELECT thread_id FROM messages WHERE id=?" << mid << cppdb::row;
			if(r.empty()) {
				response().make_error_response(404);
				return;
			}
			int tid = r.get<int>(0);
			// we want this as string for trigger
			std::string stid = r.get<std::string>(0);
			r.clear();
			sql<<	"INSERT INTO messages(reply_to,thread_id,author,content) "
				"VALUES(?,?,?,?) " 
				<< mid << tid << c.form.author.value() << c.form.comment.value() 
				<< cppdb::exec;

			cache().rise("thread_" + stid);
			tr.commit();

			response().set_redirect_header(url("/user_thread",tid));
			return;
		}
	}

	cppdb::result r;
	r=sql<<	"SELECT threads.id,author,content,title "
		"FROM messages "
		"JOIN threads ON thread_id=threads.id "
		"WHERE messages.id=?" << mid << cppdb::row;
	if(r.empty()) {
		response().make_error_response(404);
		return;
	}

	int tid;

	r>>tid>>c.author>>c.content>>c.title;
	c.thread_id = tid;
	c.msg_id = mid;
	
	if(!thread_shared::prepare(c,tid))
		return;

	render("reply",c);
}

} // namespace apps
