/*  AMX Mod X
*   Nextmap Chooser Plugin
*
* by the AMX Mod X Development Team
*  originally developed by OLO
*
* This file is part of AMX Mod X.
*
*
*  This program is free software; you can redistribute it and/or modify it
*  under the terms of the GNU General Public License as published by the
*  Free Software Foundation; either version 2 of the License, or (at
*  your option) any later version.
*
*  This program is distributed in the hope that it will be useful, but
*  WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software Foundation,
*  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*  In addition, as a special exception, the author gives permission to
*  link the code of this program with the Half-Life Game Engine ("HL
*  Engine") and Modified Game Libraries ("MODs") developed by Valve,
*  L.L.C ("Valve"). You must obey the GNU General Public License in all
*  respects for all of the code used other than the HL Engine and MODs
*  from Valve. If you modify this file, you may extend this exception
*  to your version of the file, but you are not obligated to do so. If
*  you do not wish to do so, delete this exception statement from your
*  version.
*  Cvars:
*  amx_extendmap_max <time in mins.> - max. time for overall extending
*  amx_extendmap_step <time in mins.> - with what time the map will be extended
*  amx_ext_round_max <number of possible polls for extend> - how many times the map can be extended 
*  amx_ext_round_step <number of rounds> - with what rounds number the map will be extended
*  amx_ext_win_max <number of possible polls for extend> - how many times the map can be extended 
*  amx_ext_win_step <number of rounds> - with what rounds number the map will be extended
*  amx_use_mapcycle <0|1> - use mapcycle (1) or maps.ini (0)
*/

#include <amxmodx>
#include <amxmisc>

#define MAX_MAPS  256
#define SELECTMAPS  5

#define MAP_HISTORY  5
new g_hist_mapName[MAP_HISTORY][32]

new g_mapName[MAX_MAPS][32]
new g_mapNums

new g_nextName[SELECTMAPS]
new g_voteCount[SELECTMAPS+2]
new g_mapVoteNum
new g_teamScore[2]
new g_lastMap[32]
new g_maphistFile[64]

new g_coloredMenus
new bool:g_selected = false
new bool:g_call_say_vote = false
new bool:g_vote_finished = true
new bool:g_buyingtime = true
new g_logFile[16]
new Float:amx_ext_count = 0.0


public plugin_init()
{
	register_plugin("Nextmap Chooser 4",AMXX_VERSION_STR,"AMX X Dev & KWo")
	register_dictionary("mapchooser4.txt")
	register_dictionary("common.txt")  
	register_event("TextMsg","eRestart","a","2&#Game_C","2&#Game_w")
	register_event( "SendAudio", "eEndRound", "a", 
                  "2=%!MRAD_terwin", "2=%!MRAD_ctwin", "2=%!MRAD_rounddraw" )

	register_cvar("amx_extendmap_max","90")
	register_cvar("amx_extendmap_step","15")
	register_cvar("amx_ext_round_max","3")
	register_cvar("amx_ext_round_step","3")
	register_cvar("amx_ext_win_max","3")
	register_cvar("amx_ext_win_step","3")
	register_cvar("amx_use_mapcycle","0")
	register_cvar("amx_vote_time","20")
	register_cvar("amx_vote_delay","60")
	set_cvar_float("amx_last_voting",0.0)
	if ( cstrike_running() )
		register_event("TeamScore", "team_score", "a")

	get_localinfo("lastMap",g_lastMap,31)
	set_localinfo("lastMap","")

	get_configsdir(g_maphistFile,63)
	format(g_maphistFile, 63, "%s/maphist.ini", g_maphistFile)
	load_history(g_maphistFile)
	new MenuName[64]
	format(MenuName,63,"%L","en","CHOOSE_NEXTM")
	register_menucmd(register_menuid(MenuName),(-1^(-1<<(SELECTMAPS+2))),"countVote")
	register_clcmd("say votenext","setSayVote")
	register_clcmd("say_team votenext","setSayVote")

	set_task(15.0,"voteNextmap",987456,"",0,"b")
  
	g_coloredMenus = colored_menus()      
	g_buyingtime = true
}

public checkVotes()
{
	new b = 0
	new Float:timelimit = get_cvar_float("mp_timelimit")
	new Float:maxrounds = get_cvar_float("mp_maxrounds")
	new Float:winlimit = get_cvar_float("mp_winlimit")
	for	(new a = 0; a < g_mapVoteNum; ++a)
		if (g_voteCount[b] < g_voteCount[a])
			b = a
	if ( (timelimit > 0) && ( g_voteCount[SELECTMAPS] > g_voteCount[b] ) ) 
	{
		new mapname1[32]
		get_mapname(mapname1,31)
		new Float:steptime = get_cvar_float("amx_extendmap_step")
		set_cvar_float("mp_timelimit", get_cvar_float("mp_timelimit") + steptime )
		client_print(0,print_chat,"%L", LANG_PLAYER, "CHO_FIN_EXT_T", steptime )
		log_amx("Vote: Voting for the nextmap finished. Map %s will be extended to next %.0f minutes",
			mapname1 , steptime )
		g_vote_finished = true
		return
	}

	if ( ( maxrounds > 0 ) && ( g_voteCount[SELECTMAPS] > g_voteCount[b] ) )
	{
		new mapname2[32]
		get_mapname(mapname2,31)
		new Float:stepround = get_cvar_float("amx_ext_round_step")
		set_cvar_float("mp_maxrounds", maxrounds + stepround )      
		client_print(0,print_chat,"%L", LANG_PLAYER, "CHO_FIN_EXT_R", stepround  )
		log_amx("Vote: Voting for the nextmap finished. Map %s will be extended to next %.0f rounds",
			mapname2 , stepround )
		g_vote_finished = true

		++amx_ext_count
		log_amx("Vote: Map %s will be extended %.0f time(s)",    
			mapname2 , amx_ext_count )
		client_print(0,print_chat,"%L", LANG_PLAYER, "CHO_FIN_EXT_HMT", mapname2 , amx_ext_count )         
		return
	}

	if ( (winlimit > 0 ) && ( g_voteCount[SELECTMAPS] > g_voteCount[b] ) )
	{
		new mapname3[32]
		get_mapname(mapname3,31)
		new Float:stepwin = get_cvar_float("amx_ext_win_step")
		set_cvar_float("mp_winlimit", winlimit + get_cvar_float("amx_ext_win_step") )
		client_print(0,print_chat,"%L", LANG_PLAYER, "CHO_FIN_EXT_W", stepwin )
		log_amx("Vote: Voting for the nextmap finished. Map %s will be extended to next %.0f wins",
			mapname3 , stepwin )
		g_vote_finished = true

		++amx_ext_count
		log_to_file(g_logFile,"Vote: Map %s will be extended %.0f time(s)",
			mapname3 , amx_ext_count )
		client_print(0,print_chat,"%L", LANG_PLAYER, "CHO_FIN_EXT_HMT", mapname3 , amx_ext_count )         
		return
	}

	if ( g_voteCount[b] && g_voteCount[SELECTMAPS+1] <= g_voteCount[b] )
		set_cvar_string("amx_nextmap", g_mapName[g_nextName[b]] )
	new smap[32]
	get_cvar_string("amx_nextmap",smap,31)
	client_print(0,print_chat,"%L", LANG_PLAYER, "CHO_FIN_NEXT", smap )
	log_amx("Vote: Voting for the nextmap finished. The nextmap will be %s", smap)
	g_vote_finished = true
}

public countVote(id,key)
{
	if ( get_cvar_float("amx_vote_answers") && (!g_vote_finished) )
	{
		new name[32]
		get_user_name(id,name,31)
		if ( key == SELECTMAPS )
			client_print(0,print_chat,"%L", LANG_PLAYER, "CHOSE_EXT", name )
		else if ( key < SELECTMAPS )
			client_print(0,print_chat,"%L", LANG_PLAYER, "X_CHOSE_X", name, g_mapName[g_nextName[key]] )
	}
	++g_voteCount[key]
	return PLUGIN_HANDLED
}

bool:isInMenu(id)
{
	for(new a=0; a<g_mapVoteNum; ++a)
		if (id==g_nextName[a])
			return true
	return false
}

public voteNextmap(filename[])
{
	if ( (g_mapNums == 0) )
	{
		if (task_exists(987456)) remove_task(987456)
		if (task_exists(987457)) remove_task(987457)
		return
	}
	if (g_buyingtime)
	{
		return
	}

	new timelimit2 = get_cvar_num("mp_timelimit")
	new maxrounds2 = get_cvar_num("mp_maxrounds")
	new winlimit2 = get_cvar_num("mp_winlimit")
	new Float:ext_time_max2 = get_cvar_float("amx_extendmap_max")

	if ( winlimit2 > 0 )
	{
		new c = winlimit2 - 2
		if ( ( g_teamScore[0] < c ) && ( g_teamScore[1] < c ) && ( !g_call_say_vote ) )
		{
			g_selected = false
			return
		}
	}
	else  if ( maxrounds2 > 0 ) 
	{
		new d = maxrounds2 - 2
		if ( ( ( g_teamScore[0] + g_teamScore[1] ) < d ) && ( !g_call_say_vote ) )
		{
			g_selected = false
			return
		}
	}
	else 
	{
		new timeleft = get_timeleft()
		if ( ( timeleft<1 || timeleft>129 ) && ( !g_call_say_vote) )
		{
			g_selected = false
			return
		}
	}
	if ( (g_selected) && ( !g_call_say_vote) )
		return
	g_selected = true  
	g_vote_finished = false

	new menu[512], a, mkeys = (1<<SELECTMAPS+1)
	new pos = format(menu,511,g_coloredMenus ? "\y%L:\w^n^n" : "%L:^n^n", LANG_SERVER, "CHOOSE_NEXTM")  

// map selection to menu

	new dmax = (g_mapNums - MAP_HISTORY > SELECTMAPS) ? SELECTMAPS : g_mapNums - MAP_HISTORY
	new smap2[32]
	new smap3[32]
	new nmap_pos=0
	get_cvar_string("amx_nextmap",smap2,31)

// search the position of nextmap in the list

	for	(new c=0;c<g_mapNums-1;++c) 
	{
		if ( equali(g_mapName[c],smap2) ) nmap_pos = c
	}
// first position - name of the nextmap from mapcycle  
	g_nextName[0] = nmap_pos

	for	(g_mapVoteNum = 0;	g_mapVoteNum<dmax;	++g_mapVoteNum)
	{
		a=random_num(0,g_mapNums-1)
		smap3 = g_mapName[a]
		new b = 0
		new a_hist = false
		new currentMap[32]
		get_mapname(currentMap,31)

		for (new i = 0; i < MAP_HISTORY; i++) 
		{
			if (equali(g_mapName[a],g_hist_mapName[i])) a_hist = true
		}
		while( (b < 100) && ( (g_mapVoteNum > 0) 
		&& ( isInMenu(a) || equali(g_lastMap,smap3) || equali(currentMap,smap3) || equali(smap2,smap3) || a_hist ) ) ) 
		{
			++a
			++b
			if ( a >= g_mapNums ) a = 0    
			a_hist = false
			for (new i = 0; i < MAP_HISTORY; i++) 
			{
				if (equali(g_mapName[a],g_hist_mapName[i])) a_hist = true
			}
			smap3 = g_mapName[a]
		}  
		if (g_mapVoteNum > 0) 
			g_nextName[g_mapVoteNum] = a

// use nextmap from mapcycle as 1'st selection    
		if (g_mapVoteNum==0)
		{
			pos += format(menu[pos],511,"1. %s^n",smap2)
		}
		else 
		{
			pos += format(menu[pos],511,"%d. %s^n",g_mapVoteNum+1,g_mapName[a])
		}
		mkeys |= (1<<g_mapVoteNum)
		g_voteCount[g_mapVoteNum] = 0
	}
	menu[pos++]='^n'
	g_voteCount[SELECTMAPS] = 0
	g_voteCount[SELECTMAPS+1] = 0
	new mapname[32]
	get_mapname(mapname,31)

	if ( ( ( winlimit2 + maxrounds2 )==0 ) && ( timelimit2  <  ext_time_max2 ) )
	{
		pos += format(menu[pos],511,"%d. %L^n",SELECTMAPS+1,LANG_SERVER,"EXTED_MAP",mapname)  
		mkeys |= (1<<SELECTMAPS)
	}

// new code for maxrounds and winlimit

	if ( ( timelimit2 == 0 ) && ( maxrounds2 > 0 )  
		&& ( ( g_teamScore[0] + g_teamScore[1] ) >= ( maxrounds2 - 2 ) ) 
		&& (  amx_ext_count  < ( get_cvar_num("amx_ext_round_max" ) ) ) )
	{
		pos += format(menu[pos],511,"%d. %L^n",SELECTMAPS+1,LANG_SERVER,"EXTED_MAP",mapname)    
		mkeys |= (1<<SELECTMAPS)
	}

	if ( ( timelimit2 ==0 ) && ( winlimit2 > 0 ) 
		&& ( ( ( g_teamScore[0] ) >= ( winlimit2 - 2 ) ) || ( ( g_teamScore[1] ) >= ( winlimit2 - 2 ) ) ) 
		&& ( amx_ext_count < ( get_cvar_num("amx_ext_win_max") ) ) )
	{
		pos += format(menu[pos],511,"%d. %L^n",SELECTMAPS+1,LANG_SERVER,"EXTED_MAP",mapname)    
		mkeys |= (1<<SELECTMAPS)
	}

	new Float:vote_time = get_cvar_float("amx_vote_time") + 2.0 
	new iVoteTime = floatround( vote_time )
	format(menu[pos],511,"%d. %L",SELECTMAPS+2,LANG_SERVER,"NONE")  
	new MenuName[64]
	format(MenuName,63,"%L","en","CHOOSE_NEXTM")
	show_menu(0,mkeys,menu,iVoteTime,MenuName)

	set_task(vote_time,"checkVotes")  
	if ( !g_call_say_vote )
	{
		client_print(0,print_chat,"%L",LANG_SERVER,"TIME_CHOOSE")  
	}
	client_cmd(0,"spk Gman/Gman_Choose2")
	log_amx("Vote: Voting for the nextmap started")
	g_call_say_vote = false
	if (task_exists(987457)) remove_task(987457)
}

load_history(filename[])
{
	if (!file_exists(filename)) return 0

	new a = 0
	for (new pos = 0; pos < MAP_HISTORY; pos++)
		read_file(filename,pos,g_hist_mapName[pos],31,a)

	return 1
}

loadSettings(filename[])
{
	if (!file_exists(filename)) return 0

	g_mapNums = 0
	new szText[32]
	new a = 0, pos = 0
	new currentMap[32]
	get_mapname(currentMap,31)

	while ( (g_mapNums < MAX_MAPS) && read_file(filename,pos,szText,31,a) )
	{
		if ( szText[0] != ';'
			&&  parse(szText, g_mapName[g_mapNums] ,31 ) 
			&&  is_map_valid( g_mapName[g_mapNums] ) )
// these 2 below have to be commented out - we need to know the exact number of all maps we have
// (including the current and the last one)...
// it's valid only for this version of the plugin !!!
//    &&  !equali( g_mapName[g_mapNums] ,g_lastMap)
//    &&  !equali( g_mapName[g_mapNums] ,currentMap)
		{
			++g_mapNums
		}
		pos++
	}

  //for (new i = 0; i < g_mapNums; i++){
  //  log_message("[AMXX] Map %s nr %d", g_mapName[i], i)
  //}
	log_message("[AMXX] Number of accessible maps %d", g_mapNums)
	return 1
}

public team_score()
{
	new team[2]
	read_data(1,team,1)
	g_teamScore[ (team[0]=='C') ? 0 : 1 ] = read_data(2)
}

public plugin_end()
{
	new current_map[32]
	get_mapname(current_map,31 )
	set_localinfo("lastMap",current_map)

	if (file_exists(g_maphistFile))
	{
		new text[32]
		new a = 0
		// shift list up 1
		for (new pos = 0; pos < MAP_HISTORY; pos++)
		{
			read_file(g_maphistFile,pos+1,text,31,a)
			write_file(g_maphistFile,text,pos)
		}
	}
	write_file(g_maphistFile,current_map,MAP_HISTORY-1)  
}

public eRestart()
{
	new filename[64]
	new cfgdir[64]
	get_configsdir(cfgdir, 63)
	format(filename, 63, "%s/maps.ini", cfgdir)  
    
	if ( !(get_cvar_float("amx_use_mapcycle")) && file_exists(filename) ) 
	{
		if (loadSettings( filename )) 
			log_message("[AMXX] Success - maps.ini loaded.")    
		else 
		{
			log_message("[AMXX] File maps.ini not found. Mapcycle will be loaded")
			get_cvar_string("mapcyclefile",filename,63)
			if (loadSettings( filename )) 
				log_message("[AMXX] Success - mapcycle loaded.") 
			else 
			{
				log_message("[AMXX] File mapcycle not found. No maps loaded.")
			}
		}
	}
	else
	{  
		get_cvar_string("mapcyclefile",filename,63)      
		if (loadSettings( filename )) 
			log_message("[AMXX] Success - mapcycle loaded.") 
		else 
			log_message("[AMXX] File mapcycle not found. No maps loaded.")             
	}
	amx_ext_count = 0.0    
	return filename[63]
}

public setSayVote(id)
{
	new Float:voting = get_cvar_float("amx_last_voting") + get_cvar_float("amx_vote_time")
	if ( voting > get_gametime() )
	{
		client_print(id,print_chat,"%L",LANG_PLAYER,"ALEADY_VOTING")
		return
	}
	if (( voting && voting + get_cvar_float("amx_vote_delay") > get_gametime() ) || g_buyingtime)
	{
		client_print(id,print_chat,"%L",LANG_PLAYER,"VOT_NOT_ALLOWED")
		return
	}

	new Float:vote_time2 = get_cvar_float("amx_vote_time") + 2.0 
	set_cvar_float("amx_last_voting",  get_gametime() + vote_time2 )  
	client_print(id,print_chat,"%L",LANG_PLAYER,"VOTING_STARTED")
	g_call_say_vote = true
	set_task(5.0,"voteNextmap",987457)
	return
}

public eEndRound()
{
	if (!task_exists(987400)) set_task(15.0,"buyFinished",987400)
	g_buyingtime = true
	return
}

public buyFinished()
{
	g_buyingtime = false
	return
}
