#include <amxmodx>
#include <amxmisc>

// =========================================== CONFIG START ===========================================
#define CHATVIEWER_ACCESS (ADMIN_ADMIN|ADMIN_CHAT|ADMIN_SLAY) // access to chatviewer command
#define NO_ADMIN_VIEW  // comment this line if you want admins to be able in seeing all chat
//#define ADMIN_TAG_C  // uncomment this line if you want admins to have [ADMIN] tag in chat
//#define ADMIN_TAG_R  // uncomment this line if you want admins to have [ADMIN] tag in radio messages
#define ADMIN_TAG "[ADMIN] " // translation for admin tag
#define ACTIVITY_ADMIN // comment this line if you want admin tag to be showned to all players
#define USE_RADIO_MSG  // comment this line if you don't want to change radio msg text
#define RADIO_HE " [explosive]" // translation for explosive grenade in radio
#define RADIO_SG " [smokegren]" // translation for smoke grenade in radio
#define RADIO_FB " [flashbang]" // translation for flashbang grenade in radio
// ============================================ CONFIG END ============================================

#if !defined NO_ADMIN_VIEW
	#define PLUGIN_NAME "Pro Chat"
#else
	#define PLUGIN_NAME "Pro Chat (no viewer)"
#endif
#define PLUGIN_VERSION "1.8"
#define PLUGIN_AUTHOR  "Numb"

#define print_chat_colored 5 // did you know about this? - well, I didn't

#if !defined NO_ADMIN_VIEW
#if !defined _numbamx_included
/**
 * This is for advanced flag check
 * for things like "get_user_flags(id) & (ADMIN_KICK|ADMIN_BAN|ADMIN_ADMIN)"
 * so insted of making "get_user_flags(id)&ADMIN_KICK && get_user_flags(id)&ADMIN_BAN && is_user_admin(id)"
 * use "check_flags(get_user_flags(id), ADMIN_KICK|ADMIN_BAN|ADMIN_ADMIN, true)!=0"
 *
 * NOTE: works only for worded flags (from 'a' like "ADMIN_IMMUNITY" to 'z' like "ADMIN_USER")
 *       else it will just return -1
 */
check_flags(main_flags, flags_to_check, bool:adminflags=false)
{
	new __main_flags=main_flags, __flags_to_check=flags_to_check;
	
	if (__main_flags < 0)
		__main_flags = 0;
	if (__flags_to_check < 0)
		__flags_to_check = 0; // I don't wanna take the risk & see what will happen if not doing this...
	
	if ((__main_flags == __flags_to_check) || !__flags_to_check)
		return 1;
	else if (!__main_flags)
		return 0;
	
	if (adminflags)
	{
		if (__flags_to_check&ADMIN_ADMIN)
		{
			if (__main_flags&ADMIN_USER)
				return 0;
			
			__flags_to_check-=ADMIN_ADMIN;
		}
	}
	
	if ((__main_flags&__flags_to_check)==__flags_to_check)
		return 1;
	
	return 0;
}
#endif

new bool:g_bManualChatViewer[33];
new bool:g_bUserChatViewEnabled[33];
new g_iMsgId_SayText;
new g_iChatViewerStrings[2][129];
new g_iMaxPlayers;
#endif
new g_iOldMessage[192];
new g_iLastMsg[192];
new g_iLastText[192];
#if defined ADMIN_TAG_C
new bool:g_bIsUserAdmin[33];
new g_iUserAdminName[33][44];
#else
#if defined ADMIN_TAG_R
new bool:g_bIsUserAdmin[33];
new g_iUserAdminName[33][44];
#endif
#endif
new g_iUserName[33][32];

public plugin_init()
{
	register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR);
	
#if !defined NO_ADMIN_VIEW
	g_iMaxPlayers = clamp(get_maxplayers(), 1, 32);
	
	copy(g_iChatViewerStrings[0], 128, "* Chat viewer enabled. To disable type ^"/chatviewer^".");
	copy(g_iChatViewerStrings[1], 128, "* Chat viewer disabled. To enable type ^"/chatviewer^".");
	
	register_clcmd("say",      "cmd_shatmsg");
	register_clcmd("say_team", "cmd_shatmsg");
	
	register_clcmd("say /chatviewer", "cmd_temp", CHATVIEWER_ACCESS, " - enables/disables chat viewer");
	
	register_message((g_iMsgId_SayText=get_user_msgid("SayText")), "Message_SayText");
#else
	register_message(get_user_msgid("SayText"), "Message_SayText");
#endif
#if defined USE_RADIO_MSG
	register_message(get_user_msgid("TextMsg"), "Message_TextMsg");
#endif
}

public client_putinserver(iPlrId)
{
	get_user_info(iPlrId, "name", g_iUserName[iPlrId], 31);
	
#if !defined NO_ADMIN_VIEW
	g_bManualChatViewer[iPlrId] = true;
	g_bUserChatViewEnabled[iPlrId] = (!is_user_bot(iPlrId)&&(check_flags(get_user_flags(iPlrId), CHATVIEWER_ACCESS, true)||is_user_hltv(iPlrId)))?true:false;
#endif
#if defined ADMIN_TAG_C
	g_bIsUserAdmin[iPlrId] = is_user_admin(iPlrId)?true:false;
	formatex(g_iUserAdminName[iPlrId], 43, "%s%s", ADMIN_TAG, g_iUserName[iPlrId]);
#else
#if defined ADMIN_TAG_R
	g_bIsUserAdmin[iPlrId] = is_user_admin(iPlrId)?true:false;
	formatex(g_iUserAdminName[iPlrId], 43, "%s%s", ADMIN_TAG, g_iUserName[iPlrId]);
#endif
#endif
}

public client_infochanged(iPlrId)
{
	if( !is_user_connected(iPlrId) )
		return PLUGIN_CONTINUE;
	
	get_user_info(iPlrId, "name", g_iUserName[iPlrId], 31);
#if defined ADMIN_TAG_C
	g_bIsUserAdmin[iPlrId] = is_user_admin(iPlrId)?true:false;
	formatex(g_iUserAdminName[iPlrId], 43, "%s%s", ADMIN_TAG, g_iUserName[iPlrId]);
#else
#if defined ADMIN_TAG_R
	g_bIsUserAdmin[iPlrId] = is_user_admin(iPlrId)?true:false;
	formatex(g_iUserAdminName[iPlrId], 43, "%s%s", ADMIN_TAG, g_iUserName[iPlrId]);
#endif
#endif
	if( is_user_hltv(iPlrId) || is_user_bot(iPlrId) )
		return PLUGIN_CONTINUE;
	
#if !defined NO_ADMIN_VIEW
	new iOldName[32];
	get_user_name(iPlrId, iOldName, 31);
	if( !equal(iOldName, g_iUserName[iPlrId]) )
	{
		if( check_flags(get_user_flags(iPlrId), CHATVIEWER_ACCESS, true) && g_bManualChatViewer[iPlrId] )
			g_bUserChatViewEnabled[iPlrId] = true;
		else
			g_bUserChatViewEnabled[iPlrId] = false;
	}
#endif
	return PLUGIN_CONTINUE;
}

#if !defined NO_ADMIN_VIEW
public client_disconnect(iPlrId)
{
	g_bUserChatViewEnabled[iPlrId] = false;
	g_bManualChatViewer[iPlrId] = false;
}

public cmd_temp(iPlrId, level, cid) { }

public cmd_shatmsg(iPlrId)
{
	static iArg[17];
	if( read_argv(1, iArg, 16) )
	{
		if( (iArg[0]=='.' || iArg[0]=='/')
		 && equali(iArg[1], "chatviewer") )
			return cmd_chatviewer(iPlrId);
	}
	
	return PLUGIN_CONTINUE;
}

cmd_chatviewer(iPlrId)
{
	if( is_user_bot(iPlrId) || is_user_hltv(iPlrId) )
		return PLUGIN_CONTINUE;
	
	if( !check_flags(get_user_flags(iPlrId), CHATVIEWER_ACCESS, true) )
	{
		g_bUserChatViewEnabled[iPlrId] = false;
		
		return PLUGIN_CONTINUE;
	}
	
	g_bUserChatViewEnabled[iPlrId] = g_bUserChatViewEnabled[iPlrId]?false:true;
	g_bManualChatViewer[iPlrId] = g_bUserChatViewEnabled[iPlrId];
	
	message_begin(MSG_ONE, g_iMsgId_SayText, _, iPlrId);
	write_byte(iPlrId);
	write_string(g_bUserChatViewEnabled[iPlrId]?g_iChatViewerStrings[0]:g_iChatViewerStrings[1]);
	message_end();
	
	return PLUGIN_HANDLED;
}
#endif

#if defined USE_RADIO_MSG
public Message_TextMsg(iMesgId, iMsgType, iPlrId) // I am a bit amazed, cuz didn't knew that we can use colorchat in TextMsg event
{
	// arg1: int: print_type (print_notify=1; print_console=2; print_chat=3; print_center=4; print_chat_colored=5; anymore?)
	// arg2: str: sender id [what on earth? why string?]
	// arg3: str: message
	// arg4: str: msg_arg1 (%s1 in message)
	// arg5: str: msg_arg2 (%s2 in message)
	// arg6: str: msg_arg3 (%s3 in message) [???not sure???] {isn't always sended - like in SayText}
	
	if( get_msg_args()!=5 || get_msg_arg_int(1)!=print_chat_colored )
		return PLUGIN_CONTINUE;
	
#if defined ADMIN_TAG_R
	static s_iSenderId;
#endif
	static s_iMessage[192];
	get_msg_arg_string(3, s_iMessage, 191);
	if( equal(s_iMessage, "#Game_radio") )
	{
		get_msg_arg_string(5, s_iMessage, 191);
		if( equal(s_iMessage, "#Fire_in_the_hole") )
		{
			static s_iSender[4];
			get_msg_arg_string(2, s_iSender, 3);
#if defined ADMIN_TAG_R
			switch( get_user_weapon((s_iSenderId=str_to_num(s_iSender))) )
#else
			switch( get_user_weapon(str_to_num(s_iSender)) )
#endif
			{
				case CSW_HEGRENADE:    formatex(s_iMessage, 191, "%%s1 (RADIO) :  %%s2%s", RADIO_HE);
				case CSW_SMOKEGRENADE: formatex(s_iMessage, 191, "%%s1 (RADIO) :  %%s2%s", RADIO_SG);
				case CSW_FLASHBANG:    formatex(s_iMessage, 191, "%%s1 (RADIO) :  %%s2%s", RADIO_FB);
				default:               copy(s_iMessage, 191, "%s1 (RADIO) :  %s2");
			}
			
			set_msg_arg_string(3, s_iMessage);
		}
		else
		{
			set_msg_arg_string(3, "%s1 (RADIO) :  %s2");
#if defined ADMIN_TAG_R
			static s_iSender[4];
			get_msg_arg_string(2, s_iSender, 3);
			s_iSenderId = str_to_num(s_iSender);
#endif
		}
		
#if defined ADMIN_TAG_R
		if( g_bIsUserAdmin[s_iSenderId] )
		{
#if defined ACTIVITY_ADMIN
			if( g_bIsUserAdmin[iPlrId] )
				set_msg_arg_string(4, g_iUserAdminName[s_iSenderId]);
#else
			set_msg_arg_string(4, g_iUserAdminName[s_iSenderId]);
#endif
		}
#endif
	}
	
	return PLUGIN_CONTINUE;
}
#endif

public Message_SayText(iMsgId, iMsgType, iPlrId)
{
	if( get_msg_args()!=4 )
		return PLUGIN_CONTINUE;
	
#if !defined NO_ADMIN_VIEW
	static bool:s_bCheckMsg;
#endif
	static s_iMessage[192], s_iText[192], s_iSenderId;
	get_msg_arg_string(2, s_iMessage, 191);
	get_msg_arg_string(4, s_iText, 191);
	s_iSenderId = get_msg_arg_int(1);
	
	if( g_iOldMessage[0] && equal(s_iMessage, g_iLastMsg) && equal(s_iText, g_iLastText) )
	{
		if( g_iOldMessage[0]==-1 )
			return PLUGIN_HANDLED;
#if defined ADMIN_TAG_C
		if( g_bIsUserAdmin[s_iSenderId] )
		{
#if defined ACTIVITY_ADMIN
			if( g_bIsUserAdmin[iPlrId] )
				set_msg_arg_string(3, g_iUserAdminName[s_iSenderId]);
			else
				set_msg_arg_string(3, g_iUserName[s_iSenderId]);
#else
			set_msg_arg_string(3, g_iUserAdminName[s_iSenderId]);
#endif
		}
		else
			set_msg_arg_string(3, g_iUserName[s_iSenderId]);
#else
		set_msg_arg_string(3, g_iUserName[s_iSenderId]);
#endif
		set_msg_arg_string(2, g_iOldMessage);
		set_msg_arg_string(4, "");
#if defined NO_ADMIN_VIEW
		return PLUGIN_CONTINUE;
#else
		if( s_iSenderId!=iPlrId )
			return PLUGIN_CONTINUE;
		
		s_bCheckMsg = false;
#endif
	}
	else
	{
#if !defined NO_ADMIN_VIEW
		s_bCheckMsg = true;
#endif
		g_iLastMsg = s_iMessage; // this is much faster than "copy()"
		g_iLastText = s_iText; // this is much faster than "copy()"
	}
	
#if defined NO_ADMIN_VIEW
	new iReturned;
	iReturned = format_message(s_iMessage, 191, s_iText);
	switch( replace_arguments(iReturned, s_iMessage) )
	{
		case PLUGIN_CONTINUE: return PLUGIN_CONTINUE;
		case PLUGIN_HANDLED:  return PLUGIN_HANDLED;
	}
#else
	static s_iReturned[3];
	if( s_bCheckMsg ) // cuz "s_iReturned" var is static we don't need to check team and alive status again if message is the same
	{
		s_iReturned = format_message(s_iMessage, 191, s_iText);
		switch( replace_arguments(s_iReturned[0], s_iMessage) )
		{
			case PLUGIN_CONTINUE: return PLUGIN_CONTINUE;
			case PLUGIN_HANDLED:  return PLUGIN_HANDLED;
		}
	}
#endif
#if defined ADMIN_TAG_C
	if( g_bIsUserAdmin[s_iSenderId] )
	{
#if defined ACTIVITY_ADMIN
		if( g_bIsUserAdmin[iPlrId] )
			set_msg_arg_string(3, g_iUserAdminName[s_iSenderId]);
		else
			set_msg_arg_string(3, g_iUserName[s_iSenderId]);
#else
		set_msg_arg_string(3, g_iUserAdminName[s_iSenderId]);
#endif
	}
	else
		set_msg_arg_string(3, g_iUserName[s_iSenderId]);
#else
	set_msg_arg_string(3, g_iUserName[s_iSenderId]);
#endif
#if !defined NO_ADMIN_VIEW
	if( s_iSenderId!=iPlrId )
		return PLUGIN_CONTINUE;
	
	new iAdminTeam;
	for( new iAdmin=1; iAdmin<=g_iMaxPlayers; iAdmin++ )
	{
		if( iAdmin==s_iSenderId || !g_bUserChatViewEnabled[iAdmin] )
			continue;
		
		if( s_iReturned[1] )
		{
			if( (iAdminTeam=get_user_team(iAdmin))==0 )
				iAdminTeam = 3;
		}
		
		if( iAdminTeam!=s_iReturned[1] || is_user_alive(iAdmin)!=s_iReturned[2] )
		{
			message_begin(iMsgType, iMsgId, _, iAdmin);
			write_byte(s_iSenderId);
			write_string(g_iOldMessage);

#if defined ADMIN_TAG_C
			if( g_bIsUserAdmin[s_iSenderId] )
			{
#if defined ACTIVITY_ADMIN
				if( g_bIsUserAdmin[iAdmin] )
					write_string(g_iUserAdminName[s_iSenderId]);
				else
					write_string(g_iUserName[s_iSenderId]);
#else
				write_string(g_iUserAdminName[s_iSenderId]);
#endif
			}
			else
				write_string(g_iUserName[s_iSenderId]);
#else
			write_string(g_iUserName[s_iSenderId]);
#endif
			message_end();
		}
	}
#endif
	return PLUGIN_CONTINUE;
}

replace_arguments(iReturnedValue, iMessage[192])
{
	g_iOldMessage[0] = 0;
	
	switch( iReturnedValue )
	{
		case -2:
		{
			set_msg_arg_string(2, iMessage);
			return PLUGIN_CONTINUE;
		}
		case PLUGIN_CONTINUE: return PLUGIN_CONTINUE;
		case PLUGIN_HANDLED:
		{
			g_iOldMessage[0] = -1;
			return PLUGIN_HANDLED;
		}
		default:
		{
			g_iOldMessage = iMessage; // this is much faster than "copy()"
			set_msg_arg_string(2, iMessage);
			set_msg_arg_string(4, "");
		}
	}
	
	return -1;
}
	
format_message(iMessage[], const iMsgLen, const iText[])
{
#if !defined NO_ADMIN_VIEW
	new iHandleReturn[3]; // reutrn = [0]; team = [1]; alive = [2]
#endif
	if( equal(iMessage, "#Cstrike_Name_Change") )
	{
		copy(iMessage, iMsgLen, "* %s1 changed name to %s2");
#if !defined NO_ADMIN_VIEW
		iHandleReturn[0] = -2;
		return iHandleReturn;
#else
		return -2;
#endif
	}
	
	new iLen;
	if( equal(iMessage, "#Cstrike_Chat_CT") )
	{
		iLen = copy(iMessage, iMsgLen, "(Counter-Terrorist) %s1 :  ");
#if !defined NO_ADMIN_VIEW
		iHandleReturn[1] = 2;
		iHandleReturn[2] = 1;
#endif
	}
	else if( equal(iMessage, "#Cstrike_Chat_T") )
	{
		iLen = copy(iMessage, iMsgLen, "(Terrorist) %s1 :  ");
#if !defined NO_ADMIN_VIEW
		iHandleReturn[1] = 1;
		iHandleReturn[2] = 1;
#endif
	}
	else if( equal(iMessage, "#Cstrike_Chat_CT_Dead") )
	{
		iLen = copy(iMessage, iMsgLen, "*DEAD*(CT) %s1 :  ");
#if !defined NO_ADMIN_VIEW
		iHandleReturn[1] = 2;
		iHandleReturn[2] = 0;
#endif
	}
	else if( equal(iMessage, "#Cstrike_Chat_T_Dead") )
	{
		iLen = copy(iMessage, iMsgLen, "*DEAD*(Terrorist) %s1 :  ");
#if !defined NO_ADMIN_VIEW
		iHandleReturn[1] = 1;
		iHandleReturn[2] = 0;
#endif
	}
	else if( equal(iMessage, "#Cstrike_Chat_Spec") )
	{
		iLen = copy(iMessage, iMsgLen, "(Spectator) %s1 :  ");
#if !defined NO_ADMIN_VIEW
		iHandleReturn[1] = 3;
		iHandleReturn[2] = 0;
#endif
	}
	else if( equal(iMessage, "#Cstrike_Chat_All") )
	{
		iLen = copy(iMessage, iMsgLen, "%s1 :  ");
#if !defined NO_ADMIN_VIEW
		iHandleReturn[1] = 0;
		iHandleReturn[2] = 1;
#endif
	}
	else if( equal(iMessage, "#Cstrike_Chat_AllDead") )
	{
		iLen = copy(iMessage, iMsgLen, "*DEAD* %s1 :  ");
#if !defined NO_ADMIN_VIEW
		iHandleReturn[1] = 0;
		iHandleReturn[2] = 0;
#endif
	}
	else if( equal(iMessage, "#Cstrike_Chat_AllSpec") )
	{
		iLen = copy(iMessage, iMsgLen, "*SPEC* %s1 :  ");
#if !defined NO_ADMIN_VIEW
		iHandleReturn[1] = 0;
		iHandleReturn[2] = 0;
#endif
	}
	else
	{
#if !defined NO_ADMIN_VIEW
		iHandleReturn[0] = PLUGIN_CONTINUE;
		return iHandleReturn;
#else
		return PLUGIN_CONTINUE;
#endif
	}
	
	new iLen2 = (copy(iMessage[iLen], iMsgLen-iLen, iText)-1); // last symbol is 0x10 - dunno what it is, but it dose nothing
	if( iLen2<=0 )
	{
#if !defined NO_ADMIN_VIEW
		iHandleReturn[0] = PLUGIN_HANDLED;
		return iHandleReturn;
#else
		return PLUGIN_HANDLED;
#endif
	}
	
	iLen2+=iLen;
	new bool:bWordExists;
	for( iLen+=0; iLen<iLen2; iLen++ )
	{
		if( iMessage[iLen]=='' || iMessage[iLen]=='' || iMessage[iLen]=='' || iMessage[iLen]=='' || iMessage[iLen]=='%' )
			iMessage[iLen] = ' ';
		else if( iMessage[iLen]!=' ' )
			bWordExists = true;
	}
	
	if( !bWordExists )
	{
#if !defined NO_ADMIN_VIEW
		iHandleReturn[0] = PLUGIN_HANDLED;
		return iHandleReturn;
#else
		return PLUGIN_HANDLED;
#endif
	}
	
#if !defined NO_ADMIN_VIEW
	iHandleReturn[0] = -1;
	return iHandleReturn;
#else
	return -1;
#endif
}
