#include <iostream>
#include <mysql/mysql.h>
#include "merc.h"
#include "channel.h"



int lastChan = 1;
extern MYSQL *db;
std::list<Channel *> chanList;

Command chanCmdTable[] =
{	{ "new", 	&Channel::New,		true		},
	{ "history",	&Channel::History,	false		},
	{ "ignore",	&Channel::Ignore,	true	 	},
	{ "acknowledge",&Channel::Acknowledge,	true		},
	{ "on",		&Channel::On,		false		},
	{ "off",	&Channel::Off,		false		},
	{ "lock",	&Channel::Lock,		false		},
	{ NULL, 	NULL 			}
};

Channel::Channel(char *chanName):
name(NULL),
bit(++lastChan),
lastTID(0)
{	name = str_dup(capitalize(chanName) );
}

Channel::~Channel( )
{	Topic *tpc;
	std::list<Topic *>::iterator i;
	free_string(name);
	for( i = topics.begin(); i != topics.end(); )
	{	tpc = *i;
		++i;
		delete tpc;
	}
}

void Channel::Send(ACCOUNT *pAcnt, std::string &txt)
{	std::string str;
	char buf[MSL];
	sprintf(buf, "{r[{W%s{r]{W ", name);
	str += buf;
	str += txt;
	str += "\n\r{x";
	ptc(pAcnt, (char *)str.c_str());
}

Topic * Channel::getTopic( int id )
{	std::list<Topic *>::iterator i;
	for( i = topics.begin() ; i != topics.end() ; ++i )
		if( (*i)->id == id )
			return (*i);
	return NULL;
}

void Channel::interpret(ACCOUNT *pAcnt, char *argument )
{	Post *post;
	char arg[MSL];
	char orig[MSL];
	orig[0] = '\0';
	strcpy(orig, argument);
	arg[0] = '\0';	
	argument = one_argument(argument,arg);
	
	if(arg[0] == '\0')
	{	ptc(pAcnt, "{DUse this command with {r'{W!{r'{D as your argument to view channel functions.\n\r");
		ptc(pAcnt, "To speak, use the syntax{r: '<{Wtopic id{r>{W:{r<{Wpost id{r> <{Wmessage{r>'{D\n\r");
		ptc(pAcnt, "If you don't supply a topic id, and/or a post id, it will simply\n\rpost on the last topic you spoke on, and point it to the last post on the topic.\n\r");
		if(pAcnt->lastTID[bit] == 0 )
			ptc(pAcnt, "You have yet to speak on a topic, so not supplying a TID will give you an error message.\n\r");
		else
		{	ptc(pAcnt, "If a topic id and post id are not supplied, you will post to{r: ");
			ptc(pAcnt, "{r[{W%s{r][{DT{r:{W%d{r][{DP{r:{W%d{r]{D\n\r", name, pAcnt->lastTID[bit], getTopic(pAcnt->lastTID[bit])->lastPID);
		}
		return;
	}
	if(IS_SET(pAcnt->common_flags, COMMON_BANNED) )
	{	HandleBanned(pAcnt, argument);
		return;
	}
	if(orig[0] == '!')
	{	if(!CheckChanFunc(pAcnt, &orig[1]) )
		{	int wrap=0;
			ptc(pAcnt, "{DChannel function not found, try:\n\r\n\r\t");
			for(int i = 0; chanCmdTable[i].name != NULL ; ++i )
			{	ptc(pAcnt, "{W%-13s{D", chanCmdTable[i].name);
				if( (++wrap % 3 ) == 0 ) ptc(pAcnt, "\n\r\t");
			}
			if(wrap%3) ptc(pAcnt, "\n\r");
			return;
		}
		return;
	}
	int tid = -1, pid = -1;
	char ctid[MSL], cpid[MSL];
	bool useOrig = false, tSuc = false, pSuc = false;
	ctid[0] = '\0';
	cpid[0] = '\0';
	Topic *topic = NULL;
	Post *reply;
	
	seperateArgs(arg, ctid, cpid);
	if( !is_number(ctid) )
	{	if(pAcnt->lastTID[bit] == 0 )
		{	ptc(pAcnt, "Sorry, unable to grab a default topic ID.\n\r");
			return;
		}
		tid = pAcnt->lastTID[bit];
		tSuc = true;
	}
	else
		tid = atoi(ctid);

	if( !is_number(cpid) )
	{	pSuc = true;
		pid = -2;
	}
	else
		pid = atoi(cpid);
	if(pSuc && tSuc) useOrig = true;

	if(tid == -1 || pid == -1 )
	{	ptc(pAcnt, "Please use numbers for the ID's.\n\r");
		return;
	}
	if( !(topic = getTopic(tid) ) )
	{	ptc(pAcnt, "Topic %d does not exist\n\r", tid);
		return;
	}
	else if( pid == -2 )
		pid = topic->lastPID;
	if(pSuc && argument[0] == '!' && !str_prefix( argument, "!history") )
	{	topic = getTopic(tid);
		ptc(pAcnt, "%s\n\r", argument);
		topic->PostHist(pAcnt, one_argument(argument,arg));
		return;
	}
	if(topic->locked)
	{	ptc(pAcnt, "Sorry, you can't post on a locked topic.\n\r");
		return;
	}
	if(pAcnt->isIgnoring(topic, bit) )
	{	ptc(pAcnt, "Sorry, you can't post on topics you're ignoring.\n\r");
		return;
	}
	if( !(reply = topic->getPost(pid) ) )
	{	ptc(pAcnt, "Post %d does not exist\n\r",pid);
		return;
	}
	if(!useOrig && argument[0] == '\0' )
	{	ptc(pAcnt, "Say what?\n\r");
		return;
	}
	post = new Post(reply);
	post->msg = str_dup(useOrig ? orig : argument);
	post->poster = str_dup(pAcnt->name);
	post->Send();
	pAcnt->lastTID[bit] = tid;
	return;
}

//They are so to stupid to realize that they're
//Posting to no one if they're banned. lol. I hope!

void Channel::HandleBanned(ACCOUNT *pAcnt, char *arg )
{	ptc(pAcnt, "{r[{W%s{r]{W{r[{WT{D:{W1{r][{WP{D:{W1{r]{D %s{r:{W%s", name, pAcnt->name,arg);
	return;
}

void Channel::LoadTopics()
{	MYSQL_ROW row;
	MYSQL_RES *res;
	char query[MSL];
	int maxTopic;

	sprintf(query, "SELECT * FROM Topics WHERE chanId=%d", bit );
	if( mysql_real_query(db, query, strlen(query) ) )
	{	logfp(LOG_BUG, "Channel::LoadTopics: %s", mysql_error(db) );
		return;
	}
	if( !(res = mysql_store_result(db) ) )
	{	logfp(LOG_BUG, "Channel::LoadTopics: Result null!");
		return;
	}

	if( (maxTopic = mysql_num_rows(res) ) <= 0 )
	{	logfp(LOG_BUG, "Channel::LoadTopics: No rows!" );
		mysql_free_result(res);
		return;
	}
	
	for( int i = 0; i < maxTopic ; ++i )
	{	row = mysql_fetch_row(res);
		Topic *topic;
		topic = new Topic(this);
		topic->name = str_dup(row[0]);
		topic->subject = str_dup(row[1]);
		topic->id = atoi(row[2]);
		topic->lastPID = atoi(row[4]);
		topic->LoadPosts();
	}
	mysql_free_result(res);
	return;
}
void Channel::TopicHist( ACCOUNT *pAcnt, char *argument )
{	int page=10;
	std::list<Topic *>::iterator i;
	
	if(argument[0] != '\0' && is_number(argument) )
		page = atoi(argument);
	if(page <= 0)
		page = 1;
	if(page != 1)
		ptc(pAcnt, "These are the last %d topics made on %s\n\r", page, name);
	else
		ptc(pAcnt, "This is the last topic made on %s\n\r", name);
	for(i = topics.begin() ; i != topics.end() && page > 0 ; ++i, --page)
	{	if((*i)->locked) ptc(pAcnt, "{r({WLocked{r)");
		ptc(pAcnt,"{r[{W%s{r]{W{r[{WT{D:{W%d{r]{W {D%s{r:{W%s{x\n\r", name, (*i)->id, (*i)->name,(*i)->subject);
		
	}
}

void Channel::New(ACCOUNT *pAcnt, char* argument)
{	char msg[MSL], subject[MSL], *sub, *txt;
	msg[0] = '\0';
	subject[0] = '\0';
	if(argument[0] == '\0' )
	{	ptc(pAcnt, "{DSyntax{r: {W!new {r<{Wsubject{r>{D:{r<{WContents of first post{r>\n\r");
		return;
	}

	seperateArgs(argument, subject, msg);
	sub = subject;
	txt = msg;
	while(isspace(*sub) ) ++sub;
	while(isspace(*txt) ) ++txt;
	if(*txt == '\0' || *sub == '\0' )
	{	ptc(pAcnt,"You must provide both a subject and content for the first post.\n\r");
		return;
	}
	ptc(pAcnt, "%s:%s\n\r",txt,sub);
	Topic *topic = new Topic(this);
	Post *post;
	topic->name = str_dup(pAcnt->name);
	topic->subject = str_dup(sub);
	topic->Send();
	post = new Post(topic);
	post->poster = str_dup(pAcnt->name);
	post->msg = str_dup(txt);
	post->Send();
	pAcnt->lastTID[bit] = topic->id;
	return;
}

void Channel::History(ACCOUNT *pAcnt, char* argument)
{	TopicHist(pAcnt, argument);
	return;
}

void Channel::Ignore(ACCOUNT *pAcnt, char* argument)
{	
	if(argument[0] == '\0')
	{	ptc(pAcnt, "{DSyntax{r: {W!ignore {r<{Wtopic id #{r>{x\n\r");
		ptc(pAcnt, "                list\n\r");
		return;
	}

	if(!is_number(argument ) )
	{	if(!strcasecmp(argument, "list") )
			pAcnt->showIgnore(this);
		else
			ptc(pAcnt, "You must use the topics ID to ignore it, or use list as the argument to see all ignored.\n\r");
		return;
	}
	int tid = atoi(argument);
	Topic *topic;
	if( !(topic = getTopic(tid) ) )
	{	ptc(pAcnt, "You can only ignore topics that exist.\n\r");
		return;
	}
	pAcnt->addIgnore(topic, bit);
	ptc(pAcnt, "Will now ignore all posts on the topic[%d]: %s\n\r", tid, topic->subject);
	return;
}

void Channel::Lock(ACCOUNT *pAcnt, char *argument )
{	if(argument[0] == '\0' )
	{	ptc(pAcnt, "{DSyntax{r:  {W!lock list{x\n\r");
		if(pAcnt->level > 1 )
		ptc(pAcnt, "          {r<{Wtopic id #{r>\n\r");
	}
	if(!is_number(argument ) )
	{	if(!strcasecmp(argument, "list") )
			pAcnt->showLocked(this);
		else
		{	ptc(pAcnt, "{DSyntax{r:  {W!lock list{x\n\r");
			if(pAcnt->level > 1 )
			ptc(pAcnt, "          {r<{Wtopic id #{r>\n\r");
		}
		return;
	}
	
	if(pAcnt->level <= 1 )
	{	ptc(pAcnt, "{DSyntax{r:  {W!lock list{x\n\r");
		return;
	}
	int tid = atoi(argument);
	Topic *topic;
	if( !(topic = getTopic(tid) ) )
	{	ptc(pAcnt, "You can only ignore topics that exist.\n\r");
		return;
	}
	if(topic->locked)
	{	topic->Unlock();
		ptc(pAcnt, "Topic %d on %s has been locked!\n\r", tid, name);
		return;
	}
	topic->Lock();
	ptc(pAcnt,"Topic %d on %s has been locked!\n\r", tid, name);
	return;
}
	
void Channel::Acknowledge(ACCOUNT *pAcnt, char* argument)
{	if(argument[0] == '\0')
	{	ptc(pAcnt, "{DSyntax{r: {W!acknowledge {r<{Wtopic id #{r>{x\n\r");
		return;
	}
	if(!is_number(argument ) )
	{	ptc(pAcnt, "You must use the topics ID to acknowledge it!\n\r");
		return;
	}
	int tid = atoi(argument);
	Topic *topic;
	if( !(topic = getTopic(tid) ) )
	{	ptc(pAcnt, "You can't acknowledge topics that don't exist!\n\r");
		return;
	}
	if( !pAcnt->isIgnoring(topic, bit) )
	{	ptc(pAcnt, "You have to be ignoring it to acknowledge it!\n\r");
		return;
	}
	pAcnt->remIgnore(topic, bit);
	ptc(pAcnt, "You will now acknowlege all posts on topic[%d]: %s.\n\r", tid, topic->subject);
	return;
}

void Channel::On(ACCOUNT *pAcnt, char* argument)
{	if(IS_SET(pAcnt->channel, bit) )
	{	ptc(pAcnt, "This channel is already on.\n\r");
		return;
	}
	ptc(pAcnt, "%s has been turned on.\n\r", name);
	SET_BIT(pAcnt->channel, bit);
}

void Channel::Off(ACCOUNT *pAcnt, char* argument)
{	if(!IS_SET(pAcnt->channel, bit) )
	{	ptc(pAcnt, "This channel is already off.\n\r");
		return;
	}
	ptc(pAcnt, "%s has been turned off.\n\r", name);
	UNSET_BIT(pAcnt->channel, bit);
}


Channel::Channel( const Channel &rhs )
{
}

Topic::Topic(Channel *parent ):
name(NULL),
subject(NULL),
id(++parent->lastTID),
on(parent),
lastPID(0),
locked(false)
{	on->topics.push_back(this);
}

Topic::~Topic()
{	std::list<Post *>::iterator i;
	std::list<Topic *>::iterator n;
	Post *pst;
	free_string(name);
	free_string(subject);
	for( i = posts.begin() ; i != posts.end() ; )
	{	pst = *i;
		++i;
		delete pst;
	}
	for( n = on->topics.begin() ; n != on->topics.end(); ++n )
		if( *n == this )
		{	on->topics.erase(n);
			break;
		}
}
//Output:
//[ChanName] Davion has started a new Topic[T:#]: Subject Line here.
void Topic::Send( )
{	std::string str;
	ACCOUNT *pAcnt;
	for(pAcnt = account_list ; pAcnt ; pAcnt = pAcnt->next)
	{	if(!pAcnt->canSend(this, on) )
			continue;
		str.clear();
		str = pAcnt->genChanPrefix(NULL,this);
		on->Send(pAcnt, str);
	}
	Log();
}

Topic::Topic(const Topic &rhs )
{
}

Post *Topic::getPost(int id)
{	std::list<Post *>::iterator i;
	if(id == -1 )
		return NULL;	
	for( i = posts.begin() ; i != posts.end() ; ++i )
		if( (*i)->id == id )
			return (*i);
	return NULL;
}
//Query
/*INSERT INTO `Topics` ( `name` , `subject` , `id` , `chanId` , `lastpid` )
VALUES (
'Davion', 'The subject', '1', '10', '1'
);*/
void Topic::Log()
{	char query[MSL];
	char safeSub[MSL*2];
	mysql_real_escape_string(db, safeSub, subject, strlen(subject));
	sprintf(query, "INSERT INTO `Topics` ( `name` , `subject` , `id` , `chanId` , `lastpid` ) VALUES                 ( '%s', '%s', '%d', '%d', '%d' );", name, safeSub, id, on->bit, lastPID);
	if(mysql_real_query(db, query, strlen(query) ) )
	{	logfp(LOG_BUG, "Topic::Log(): %s", mysql_error(db) );
		return;
	}
}

void Topic::LoadPosts()
{	char query[MSL];
	MYSQL_RES *res;
	MYSQL_ROW row;
	int maxPosts;
	
	sprintf(query, "SELECT * FROM Posts WHERE chanId=%d && topicid=%d", on->bit, id );
	logfp(LOG_BUG, "Topic::LoadPosts: Begining to load. %s", query );
	if( mysql_real_query(db, query, strlen(query) ) )
	{	logfp(LOG_BUG, "Topic::LoadPosts[C:%d][T:%d]: %s", id, on->bit, mysql_error(db) );
		return;
	}
	
	if( !(res = mysql_store_result(db) ) )
	{	logfp(LOG_BUG, "Topic::LoadPosts[C:%d][T:%d]: Result null!", id, on->bit );
		mysql_free_result(res);
		return;
	}
	
	if( ( maxPosts = mysql_num_rows(res) ) <= 0 )
	{	logfp(LOG_BUG, "Topic::LoadPosts[C:%d][T:%d]: No rows!", id, on->bit );
		mysql_free_result(res);
		return;
	}

	for( int i = 0; i < maxPosts ; ++i )
	{	Post *post, *rpost;
		int replyPost;
		row = mysql_fetch_row(res);
		replyPost = atoi(row[2]);
		if( replyPost != -1 )
		{	if( !( rpost = getPost(replyPost) ) )
			{	logfp(LOG_BUG, "Topic::LoadPosts: rpost is null for %d.", replyPost);
				continue;
			}
			post = new Post(rpost);
		}
		else
			post = new Post(this);
		
		post->poster = str_dup(row[0]);
		post->msg = str_dup(row[1]);
		logfp(LOG_BUG, "\tLoading: %s[%d][%d]", post->poster, post->id, replyPost);
	}
	mysql_free_result(res);
}

void Topic::PostHist( ACCOUNT *pAcnt, char *argument )
{	int page=10;
	std::list<Post *>::iterator i;
	
	if(argument[0] != '\0' && is_number(argument) )
		page = atoi(argument);
	if(page <= 0 )
		page = 1;
	if(page != 1)
		ptc(pAcnt, "These are the last %d posts made on %s under %s\n\r", page, on->name, subject);
	else
		ptc(pAcnt, "This is the last post made on %s under %s\n\r", on->name,subject);
	for(i = posts.begin() ; i != posts.end() && page > 0 ; ++i, --page)
		ptc(pAcnt,"{r{r[{W%s{r]{W%s %s\n\r", on->name, pAcnt->genChanPrefix((*i)), (*i)->msg);
}

Post::Post(Topic *parent ):
poster(NULL),
parentID(-1),
id(++parent->lastPID),
on(parent),
msg(NULL),
timeStamp(current_time)
{	on->posts.push_back(this);
}

Post::Post(Post *parent):
poster(NULL),
parentID(parent->id),
id(++parent->on->lastPID),
on(parent->on),
msg(NULL),
timeStamp(current_time)
{	parent->replies.push_back(this);
	on->posts.push_back(this);
}

Post::~Post()
{	std::list<Post *>::iterator i;
	Post *parent;
        free_string(poster);
	free_string(msg);
	for( i = on->posts.begin() ; i != on->posts.end() ; ++i )
		if(this == *i)
		{	on->posts.erase(i);
			break;
		}
	if(parentID != -1 )
	{	if( (parent = on->getPost(parentID) ) )
		{	for( i = parent->replies.begin() ; i != parent->replies.end() ; ++i )
				if((*i) == this )
				{	parent->replies.erase(i);
					break;
				}
 		}
	}
}

//Default Output
//if replying
//[ChanName][T:#][P:#] In reply to Davion[P:#] Descil says: This my reply to you!
//else
//[ChanName][T:#][P:#] Descil says: This is my post!
void Post::Send()
{	std::string str;
	ACCOUNT *pAcnt;
	for(pAcnt = account_list ; pAcnt ; pAcnt = pAcnt->next)
	{	if(!pAcnt->canSend(on, on->on) )
			continue;
		str.clear();
		str = pAcnt->genChanPrefix(this);
		str += " ";
		str += msg;
		on->on->Send(pAcnt, str);
	}	

	Log();
	return;
}
//Query
/*INSERT INTO `Posts` ( `poster` , `msg` , `replyid` , `id` , `topicid` , `timestamp` )
VALUES (
'Davion', 'This is the test message', '-1', '1', '1', NOW( )
); */
void Post::Log()
{	char query[MSL*3];
	char safeMsg[MSL*2];
	mysql_real_escape_string(db, safeMsg,msg, strlen(msg));
	sprintf(query,"INSERT INTO `Posts` ( `poster` , `msg` , `replyid` , `id` , `topicid`, `chanId`, `timestamp` )            VALUES ( '%s', '%s', '%d', '%d', '%d', '%d' ,  NOW() );", 
                       poster, safeMsg, parentID, id, on->id, on->on->bit);
	if(mysql_real_query(db, query, strlen(query)) )
	{	logfp(LOG_BUG, "Post::Log: %s\n\r\t%s", mysql_error(db), query );
		return;
	}
	return;
}