
/*
	ChannelStatus
		channelName (String)
		channelTopic (String)
		channelState ('SentJOIN' | 'Joined' | 'SentPART')
		channelUsers (Set)
			key:nick
			UserStatus
		userModes (Set)
			key:nick
			String (mode)

   UserStatus
		userNick (String)
		userChannels (Set)
			key:channel
			ChannelStatus

var myChannels;
	key:channel
	ChannelStatus

var channelUsers;
	key:nick
	UserStatus

var queryUsers;
	key:nick
	QueryStatus
		queryNick

var myNick;
*/

function LoqChatStatusMgr_DumpStatus(win)
{
	win.Write('<p>');

	win.Write('Channels<ul>');
	if (hasNoProperty(this.myChannels)) {
		win.Write('empty');
	}
	else {
		for (var ch in this.myChannels) {
			win.Write('<dl><dt>('+ch+')');
			for (var p in this.myChannels[ch]) {
				switch (p) {
				case 'channelUsers':
					win.Write('<dl><dt>('+p+')');
					for (var u in this.myChannels[ch][p]) {
						win.Write('<dd>['+u+']');
					}
					win.Write('</dl>');
					break;

				case 'userModes':
					if (! hasNoProperty(this.myChannels[ch][p])) {
						win.Write('<dl><dt>('+p+')');
						for (var u in this.myChannels[ch][p]) {
							win.Write('<dd>['+u+'] ['+this.myChannels[ch][p][u]+']');
						}
						win.Write('</dl>');
					}
					break;


				default:
					win.Write('<dd>('+p+') ['+this.myChannels[ch][p]+']');
				}
			}
			win.Write('</dl>');
		}
	}
	win.Write('</ul>');

	win.Write('Users<ul>');
	if (hasNoProperty(this.channelUsers)) {
		win.Write('empty');
	}
	else {
		for (var us in this.channelUsers) {
			win.Write('<dl><dt>('+us+')');
			for (var p in this.channelUsers[us]) {
				switch (p) {
					case 'userChannels':
					win.Write('<dl><dt>('+p+')');
					for (var c in this.channelUsers[us][p]) {
						win.Write('<dd>['+c+']');
					}
					win.Write('</dl>');
					break;

				default:
					win.Write('<dd>('+p+') ['+this.channelUsers[us][p]+']');
				}
			}
			win.Write('</dl>');
		}
	}
	win.Write('</ul>');

	win.Write('Query<ul>');
	if (hasNoProperty(this.queryUsers)) {
		win.Write('empty');
	}
	else {
		for (var us in this.queryUsers) {
			win.Write('('+us+')<br>');
		}
	}
	win.Write('</ul>');

	win.Write('<hr>');
}

function LoqChatStatusMgr_ISentJOIN(channelName)
{
	var channel = channelName.toLowerCase();

	if (this.myChannels[channel]) {
		switch (this.myChannels[channel].channelState) {
		case 'SentJOIN':
			top.DebugWindow.Error('you already sent JOIN for ['+channel+']');
			break;
		case 'Joined':
			top.DebugWindow.Error('you already joined ['+channel+']');
			break;
		case 'SentPART':
			top.DebugWindow.Error('you sent PART for ['+channel+']');
			break;
		}
		return;
	}

	this.myChannels[channel] = {
		'channelName':channelName,
		'channelState':'SentJOIN',
		'channelUsers':{},
		'userModes':{}
	};

	return this.myChannels[channel];
}

function LoqChatStatusMgr_IJoinChannel(channelName)
{
	var channel = channelName.toLowerCase();

	var channel_status = this.myChannels[channel];
	if (! channel_status || channel_status.channelState != 'SentJOIN') {
		top.DebugWindow.Error('you did not send JOIN for ['+channel+']');
		return;
	}

	this.UserJoinsChannel(this.myNick, channelName);
	channel_status.channelState = 'Joined';

	this.lastJoinedChannel = channel;

	return channel_status;
}

function LoqChatStatusMgr_GetLastJoinedChannel()
{
	return this.lastJoinedChannel;
}

function LoqChatStatusMgr_ISentPART(channelName)
{
	var channel = channelName.toLowerCase();

	var channel_status = this.myChannels[channel];
	if (! channel_status || channel_status.channelState != 'Joined') {
		top.DebugWindow.Error('you did not join ['+channel+']');
		return;
	}

	channel_status['channelState'] = 'SentPART';

	return channel_status;
}

function LoqChatStatusMgr_UserJoinsChannel(userNick, channelName)
{
	var nick = userNick.toLowerCase();
	var channel = channelName.toLowerCase();

	var channel_status = this.myChannels[channel];
	if (! channel_status) {
		top.DebugWindow.Error('you did not join ['+channel+']');
		return;
	}

	var user_status = this.channelUsers[nick];
	if (! user_status) { // user didn't join another channel
		user_status = {
			'userNick' : userNick,
			'userChannels' : {}
		};
		this.channelUsers[nick] = user_status;
	}

	if (user_status.userChannels[channel]) {
		top.DebugWindow.Error('['+nick+'] already joined ['+channel+']');
		return;
	}

	// user -> channel
	user_status.userChannels[channel] = channel_status;

	// channel -> user
	channel_status.channelUsers[nick] = user_status;

	return channel_status;
}

function LoqChatStatusMgr_IPartChannel(channelName)
{
	var nick = this.myNick.toLowerCase();
	var channel = channelName.toLowerCase();

	var channel_status = this.myChannels[channel];
	if (! channel_status) {
		top.DebugWindow.Error('you did not join ['+channel+']');
		return;
	}

	for (var nick in channel_status.channelUsers) {
		delete this.channelUsers[nick];
	}

	delete this.myChannels[channel];
}

function LoqChatStatusMgr_UserPartsChannel(userNick, channelName)
{
	var nick = userNick.toLowerCase();
	var channel = channelName.toLowerCase();

	var channel_status = this.myChannels[channel];
	if (! channel_status) {
		top.DebugWindow.Error('you did not join ['+channel+']');
		return;
	}

	var user_status = this.channelUsers[nick];
	if (! user_status) {
		top.DebugWindow.Error('invalid user ['+nick+']');
		return;
	}

	if (! channel_status.channelUsers[nick]) {
		top.DebugWindow.Error('['+nick+'] did not join ['+channel+']');
		return;
	}

	delete channel_status.channelUsers[nick];

	if (channel_status.userModes[nick])
		delete channel_status.userModes[nick];

	delete user_status.userChannels[channel];

	if (hasNoProperty(user_status.userChannels))
		delete this.channelUsers[nick];
}

function LoqChatStatusMgr_IQuit()
{
	this.myNick = null;

	delete this.myChannels;
	delete this.channelUsers;
	delete this.queryUsers;
}

function LoqChatStatusMgr_UserQuits(userNick)
{
	var nick = userNick.toLowerCase();

	var user_status = this.channelUsers[nick];
	if (! user_status) {
		top.DebugWindow.Error('invalid user ['+nick+']');
		return;
	}

	for (var channel in user_status.userChannels) {
		var channel_status = this.myChannels[channel];
		if (! channel_status) {
			top.DebugWindow.Error('INCONSISTENCY! ['+nick+'] joined ['+channel+'] does not exist');
			return;
		}

		if (! channel_status.channelUsers[nick]) {
			top.DebugWindow.Error('INCONSISTENCY! ['+channel+'] does not have ['+nick+']');
			return;
		}

		delete channel_status.channelUsers[nick];

		if (channel_status.userModes[nick])
			delete channel_status.userModes[nick];

		delete user_status.userChannels[channel];
	}

	delete this.channelUsers[nick];

	if (this.queryUsers[nick]) {
		ShutdownQuery(nick);
	}
}

function LoqChatStatusMgr_IChangeNick(newNick)
{
	var oldNick = this.myNick;

	this.myNick = newNick;
	if (oldNick && this.channelUsers && this.channelUsers[oldNick.toLowerCase()])
		return this.UserChangesNick(oldNick, newNick);
}

function LoqChatStatusMgr_UserChangesNick(userNick, newNick)
{
	var nick = userNick.toLowerCase();
	var new_nick = newNick.toLowerCase();

	var user_status = this.channelUsers[nick];
	if (! user_status) {
		top.DebugWindow.Error('invalid user ['+nick+']');
		return;
	}

	user_status.userNick = newNick;

	for (var channel in user_status.userChannels) {
		var channel_status = this.myChannels[channel];
		if (! channel_status) {
			top.DebugWindow.Error('INCONSISTENCY! ['+nick+'] joined ['+channel+'] does not exist');
			return;
		}

		if (! channel_status.channelUsers[nick]) {
			top.DebugWindow.Error('INCONSISTENCY! ['+channel+'] does not have ['+nick+']');
			return;
		}

		channel_status.channelUsers[new_nick] = channel_status.channelUsers[nick];
		delete channel_status.channelUsers[nick];

		if (channel_status.userModes[nick]) {
			channel_status.userModes[new_nick] = channel_status.userModes[nick];
			delete channel_status.userModes[nick];
		}

		delete this.channelUsers[nick];
		this.channelUsers[new_nick] = user_status;
	}

	var query_status = this.queryUsers[nick];
	if (query_status) {
		delete this.queryUsers[nick];
		this.queryUsers[new_nick] = query_status;
	}

	return user_status;
}

function LoqChatStatusMgr_ChangeTopic(channelName, newTopic)
{
	var channel = channelName.toLowerCase();

	var channel_status = this.myChannels[channel];
	if (! channel_status) {
		top.DebugWindow.Error('you did not join ['+channel+']');
		return;
	}

	channel_status.channelTopic = newTopic;
}

function LoqChatStatusMgr_ChangeUserMode(userNick, channelName, userMode)
{
	var nick = userNick.toLowerCase();
	var channel = channelName.toLowerCase();

	var channel_status = this.myChannels[channel];
	if (! channel_status) {
		top.DebugWindow.Error('you did not join ['+channel+']');
		return;
	}

	var user_status = this.channelUsers[nick];
	if (! user_status) {
		top.DebugWindow.Error('invalid user ['+nick+']');
		return;
	}

	if (! channel_status.channelUsers[nick]) {
		top.DebugWindow.Error('['+nick+'] did not join ['+channel+']');
		return;
	}

	channel_status.userModes[nick] = userMode;
}

function LoqChatStatusMgr_OpenQuery(userNick)
{
	var nick = userNick.toLowerCase();

	if (this.queryUsers[nick]) {
		top.DebugWindow.Error('query already open for ['+nick+']');
		return;
	}

	this.queryUsers[nick] = {
		'queryNick': userNick
	};

	return this.queryUsers[nick];
}

function LoqChatStatusMgr_ShutdownQuery(userNick)
{
	var nick = userNick.toLowerCase();

	if (! this.queryUsers[nick]) {
		top.DebugWindow.Error('invalid user ['+nick+']');
		return;
	}

	delete this.queryUsers[nick];
}

function LoqChatStatusMgr_GetChannelStatus(channelName)
{
	if (channelName && this.myChannels)
		return this.myChannels[channelName.toLowerCase()];
	else
		return this.myChannels;
}

function LoqChatStatusMgr_GetUserStatus(userNick)
{
	if (userNick && this.channelUsers)
		return this.channelUsers[userNick.toLowerCase()];
	else
		return this.channelUsers;
}

function LoqChatStatusMgr_GetQueryStatus(userNick)
{
	if (userNick && this.queryUsers)
		return this.queryUsers[userNick.toLowerCase()];
	else
		return this.queryUsers;
}

function LoqChatStatusMgr_Joins(userNick, channelName)
{
	var nick = userNick.toLowerCase();
	var channel = channelName.toLowerCase();

	var user_status = this.channelUsers[nick];
	if (user_status) {
		if (user_status.userChannels && user_status.userChannels[channel])
			return true;
	}

	return false;
}

function LoqChatStatusMgr_Init()
{
	this.myChannels = new Object();
	this.channelUsers = new Object();
	this.queryUsers = new Object();
	this.lastJoinedChannel = '';
}

function LoqChatStatusMgr()
{
	this.ChangeTopic = LoqChatStatusMgr_ChangeTopic;
	this.ChangeUserMode = LoqChatStatusMgr_ChangeUserMode;
	this.DumpStatus = LoqChatStatusMgr_DumpStatus;
	this.GetChannelStatus = LoqChatStatusMgr_GetChannelStatus;
	this.GetLastJoinedChannel = LoqChatStatusMgr_GetLastJoinedChannel;
	this.GetQueryStatus = LoqChatStatusMgr_GetQueryStatus;
	this.GetUserStatus = LoqChatStatusMgr_GetUserStatus;
	this.IChangeNick = LoqChatStatusMgr_IChangeNick;
	this.IJoinChannel = LoqChatStatusMgr_IJoinChannel;
	this.IPartChannel = LoqChatStatusMgr_IPartChannel;
	this.IQuit = LoqChatStatusMgr_IQuit;
	this.ISentJOIN = LoqChatStatusMgr_ISentJOIN;
	this.ISentPART = LoqChatStatusMgr_ISentPART;
	this.Init = LoqChatStatusMgr_Init;
	this.Joins = LoqChatStatusMgr_Joins;
	this.OpenQuery = LoqChatStatusMgr_OpenQuery;
	this.ShutdownQuery = LoqChatStatusMgr_ShutdownQuery;
	this.UserChangesNick = LoqChatStatusMgr_UserChangesNick;
	this.UserJoinsChannel = LoqChatStatusMgr_UserJoinsChannel;
	this.UserPartsChannel = LoqChatStatusMgr_UserPartsChannel;
	this.UserQuits = LoqChatStatusMgr_UserQuits;
}

