import es
import popuplib
import time
import playerlib
import os
import langlib

import wcs
from wcs import wcs

text = lambda x, y = {}, lang = "en": "No strings.ini located inside ../wcs/admin/"

def load():
    """ This method initializes when the script is first loaded. Assign variables and set
        up the script here... """
    global text
        
    
    """ Load up the multi-lingual strings into memory """    
    strPath = es.getAddonPath('wcs/admin') + "/strings.ini"
    if os.path.isfile(strPath):
        text = langlib.Strings(strPath)
        
    """ Assign the submenus to the main admin menu... """
    adminMenu = wcs.Admin()
    adminMenu.registerMenu('Online Users',  onlineMenu)
    adminMenu.registerMenu('Offline Users', offlineMenu)
    
    """ Register the client commands so we can have shortcuts to the menus and allow for esc input boxes. """
    clientCommandDict = {
                        'wcs_commandGiveRaceLevels'  : ['commandGiveRaceLevels',  'Give a players race additional levels'],
                        'wcs_commandGiveXp'          : ['commandGiveXp',          'Give a player some additional experience points'],
                        'wcs_commandGiveLevels'      : ['commandGiveLevels',      'Give a player some additional levels'],
                        
                        'wcs_commandRetrieveXp'      : ['commandRetrieveXp',      'Retrieve an amount of xp from a players getxp key'],
                        'wcs_commandRetrieveLevels'  : ['commandRetrieveLevels',  'Retrieve an amount of xp from a players getlevel key']
                        }
                        
    """ Iterate through the dictionary, registering the client commands to the designated methods. """
    for iterCommand in clientCommandDict:
        if not es.exists('clientcommand', iterCommand):
            es.regclientcmd(iterCommand, 'wcs/admin/%s' % clientCommandDict[iterCommand][0], clientCommandDict[iterCommand][1])
    
    """ Create the online and offline menus... """
    iterIndex   = 1
    onlineMenuPopup  = popuplib.create('wcs_onlineMenu')
    offlineMenuPopup = popuplib.create('wcs_offlineMenu')
    onlineMenuPopup.addline('WCS Admin Menu\nOnline Users\n%s' % ('-' * 25) )
    offlineMenuPopup.addline('WCS Admin Menu\nOffline Users\n%s' % ('-' * 25) )
    for option in ['Give Levels By Race', 'Give XP For getxp Say Command', 'Give Levels For getlevel Say Command', 'Set User\'s Race Level To 1', 'Set User\'s Total Level To 1']: 
        onlineMenuPopup.addline('->%s. %s' % (iterIndex, option) )
        offlineMenuPopup.addline('->%s. %s' % (iterIndex, option) )
        iterIndex += 1
    onlineMenuPopup.addline('-' * 25)
    onlineMenuPopup.addline('0. Back')
    offlineMenuPopup.addline('-' * 25)
    offlineMenuPopup.addline('0. Back')
    onlineMenuPopup.menuselect  = onlineMenuMenuSelect
    offlineMenuPopup.menuselect = offlineMenuMenuSelect 
    
def player_spawn(ev):
    """ Check if the player has any getlevel or getxp in their dictionary; if so, tell them a message """
    userid  = ev['userid']
    steamid = getDictInstance(userid)
    if steamid is not None:
        if 'getlevel' in steamid:
            if steamid['getlevel']:
                es.tell(userid, '#multi', text('get level', {'amount' : steamid['getlevel']}, playerlib.getPlayer(userid).get("lang") ) )
        if 'getxp' in steamid:
            if steamid['getxp']:
                es.tell(userid, '#multi', text('get xp', {'amount' : steamid['getxp']}, playerlib.getPlayer(userid).get("lang") ) )
    
def isAdmin(player):
    """ This function acuires the true steamid of the parameter which was passed;
        if the steamid is an admin, then return True, else False. """
    if not isOnline(player):
        return False
    steamid = getSteamId(player)
    if steamid is not None:
        """ Okay, if the steamid was changed, then we have a steamid which is on the server... """
        if steamid in wcs.wcs_admins:
            """ If the steamid is in the admin list which is defined in the main wcs.py, then pass. """
            return True
    """ If all else fails, then return False """
    return False
    
def isOnline(player):
    """ Returns True/False depnding on whether the player is currently online or not """
    return bool(es.getuserid(player))
    
def getSteamId(player):
    """ Return the steamid of the player despite the type and target I give it """
    steamid = None
    if str(player).isdigit():
        """ Okay, we were passed an integer, or a players userid; convert to steam """
        if es.exists('userid', player):
            steamid = playerlib.uniqueid(player, True)
    elif str(player).startswith(('STEAM_', 'BOT_')):
        """ We were passed a steamid, so do nothing; just check if the steamid is in the admin list """
        steamid = player
    else:
        """ Okay, we were either passed a name or something else... Check for userid via es.getuserid()"""
        tempUserid = es.getuserid(player)
        if es.exists('userid', tempUserid):
            steamid = playerlib.uniqueid(tempUserid, True)
    return steamid
    
def getDictInstance(target):
    """ This returns the players dictionary instance within the main WCS script """
    steamid = getSteamId(target)
    if steamid in wcs._dict_WCSPlayers:
        return wcs._dict_WCSPlayers[steamid]
    return None
    
def sendUnkownError(admin):
    """ A simple function so I didn't have to keep re-typing it because I'm lazy """
    es.tell(admin, '#green', text('player does not exist', {}, playerlib.getPlayer(admin).get("lang") ) )
    
def sayFilter(userid, message, teamOnly):
    """ This method hooks the say, so we can filter out messages. It is similar to es.regsaycmd() only
        we can pass arguments to functions, which seems more pythonic and proffesional. """
    message      = message.strip('"')
    textLower    = message.lower()
    if textLower == "getxp":
        if getDictInstance(userid)['getxp']:
            es.tell(userid, '#multi', text('amount of levels or xp', {'amount' : getDictInstance(userid)['getxp'], 'type' : 'experience points'}, playerlib.getPlayer(userid).get("lang") ) )
            raceMenuSelection(userid, [userid, (retrieveXp,)], None)
        else:
            es.tell(userid, '#multi', text('do not have enough', {'type' : 'experience points'}, playerlib.getPlayer(userid).get("lang") ) )
        return (0, "", 0)
    elif textLower == "getlevel":
        if getDictInstance(userid)['getlevel']:
            es.tell(userid, '#multi', text('amount of levels or xp', {'amount' : getDictInstance(userid)['getlevel'], 'type' : 'levels'}, playerlib.getPlayer(userid).get("lang") ) )
            raceMenuSelection(userid, [userid, (retrieveLevels,)], None)
        else:
            es.tell(userid, '#multi', text('do not have enough', {'type' : 'levels'}, playerlib.getPlayer(userid).get("lang") ) )
        return (0, "", 0)
    return (userid, message, teamOnly)
es.addons.registerSayFilter(sayFilter)

def raceMenuSelection(userid, choice, popupid):
    """ This method runs when a player from the offline player list has been selected """
    function        = choice[1][0]
    offlineRaceMenu = popuplib.easymenu('wcs_raceMenu_user%s' % userid, '_popup_choice', function)
    offlineRaceMenu.settitle('WCS Admin Offline Menu\nSelect a race from the player\'s list.')
    steamid         = getDictInstance(choice[0])
    for race in wcs.race_list:
        if race in steamid:
            if not wcs.canUserActivateRace(userid, race):
                continue
            offlineRaceMenu.addoption([race, choice[0]], "%s (Current Level: %s)" % (race, steamid[race]['level']) )
    offlineRaceMenu.send(userid)

def createAmountList(userid, target, function, *args):
    """ 
    This function creates a default set of values to make it 'easier' to choose a value. If the player
    wants a custom value, then they can choose the 'custom' option.
    """
    amountMenu = popuplib.easymenu('wcs_amountList_user%s' % userid, '_popup_choice', sendFunctionWithValue)
    amountMenu.settitle('Select an amount')
    amountMenu.addoption([1,    function, target, args], "1"   )
    amountMenu.addoption([2,    function, target, args], "2"   )
    amountMenu.addoption([5,    function, target, args], "5"   )
    amountMenu.addoption([10,   function, target, args], "10"  )
    amountMenu.addoption([20,   function, target, args], "20"  )
    amountMenu.addoption([50,   function, target, args], "50"  )
    amountMenu.addoption([100,  function, target, args], "100" )
    amountMenu.addoption([1000, function, target, args], "1000")
    amountMenu.addoption(["custom", function, target, args], "Custom (escape menu)")
    amountMenu.submenu(10, 'wcs_wcsadminmenu')
    amountMenu.send(userid)
    
def sendFunctionWithValue(userid, choice, popupid):
    """ This function controls which function will be sent after a player chooses a value from the 'valuelist' popup """
    amount, function, target, args = choice
    if isinstance(amount, str) and amount == "custom":
        """ Okay, we have to send them an escape input box to input a custom value """
        es.tell(userid, '#multi', text('escape', lang = playerlib.getPlayer(userid).get("lang") ) )
        if args:
            """ We got some additional args, so join them via the <string>.join(<iterable>) """
            """ Note: The Function returns <function [function name] at [memory address] > so split it and return the value in item 1"""
            es.escinputbox(30, userid, '=== WarCraft: Source ===', 'Enter the amount' , 'wcs_%s" "%s" "%s' % (str(function).split()[1], target, ' '.join(args)) )
        else:
            """ No args, so just run the command with the target """
            es.escinputbox(30, userid, '=== WarCraft: Source ===', 'Enter the amount' , 'wcs_%s" "%s' % (str(function).split()[1], target) )
    else:
        """ We're calling the local function directly and passing the values """ 
        if callable(function):
            if args:
                """ Okay we have some args, so send them unpacked """
                function(userid, target, amount, *args)
            else:
                """ No additional arguments, so we have no need to send any. """
                function(userid, target, amount)
            

""" Below here is the main 'forefront' of the script. It will contain all the popups and their selection methods """

def onlineMenu(userid):
    """ This function is ran when the player selects "online menu" from the main admin menu. """
    if isAdmin(userid):
        popuplib.send('wcs_onlineMenu', userid)
    
def onlineMenuMenuSelect(userid, choice, popupid):
    """ This method runs when an admin selects an option to do for the online players """
    if 1 <= choice <= 5:
        if choice == 1:
            createOnlinePlayerList(userid, raceMenuSelection, giveRaceLevels)
        elif choice == 2:
            createOnlinePlayerList(userid, giveXp)
        elif choice == 3:
            createOnlinePlayerList(userid, giveLevels)
        elif choice == 4:
            createOnlinePlayerList(userid, raceMenuSelection, resetRaceLevel)
        elif choice == 5:
            createOnlinePlayerList(userid, resetTotalLevel)
    elif choice == 10:
        popuplib.send('wcs_wcsadminmenu', userid)
    else:
        popuplib.send('wcs_onlineMenu', userid)
    
def createOnlinePlayerList(userid, function, *args):
    """ This function re-creates a new popup menu of all the current online players. """
    onlinePlayerList = popuplib.easymenu('wcs_onlinePlayerList_user%s' % userid, '_popup_choice', function)
    onlinePlayerList.settitle('Select a player\nPage:')
    for player in es.getUseridList():
        if args:
            onlinePlayerList.addoption([getSteamId(player), args], "%s" % es.getplayername(player))
        else:
            onlinePlayerList.addoption(getSteamId(player), "%s" % es.getplayername(player))
    onlinePlayerList.submenu(10, 'wcs_onlineMenu')
    onlinePlayerList.cc_exitformat = "0. Back"
    onlinePlayerList.send(userid)
    
def offlineMenu(userid):
    """ This function is ran when the player selects "offline menu" from the main admin menu. """
    if isAdmin(userid):
        popuplib.send('wcs_offlineMenu', userid)
    
def offlineMenuMenuSelect(userid, choice, popupid):
    """ This method runs when an admin selects an option to do for the offline players """
    if 1 <= choice <= 5:
        if choice == 1:
            createOfflinePlayerList(userid, raceMenuSelection, giveRaceLevels)
        elif choice == 2:
            createOfflinePlayerList(userid, giveXp)
        elif choice == 3:
            createOfflinePlayerList(userid, giveLevels)
        elif choice == 4:
            createOfflinePlayerList(userid, raceMenuSelection, resetRaceLevel)
        elif choice == 5:
            createOfflinePlayerList(userid, resetTotalLevel)
    elif choice == 10:
        popuplib.send('wcs_wcsadminmenu', userid)
    else:
        popuplib.send('wcs_offlineMenu', userid)
        
def createOfflinePlayerList(userid, function, *args):
    """ This function re-creates a new popup of all the players currently offline. """
    offlinePlayers    = {}
    offlinePlayerList = popuplib.easymenu('wcs_offlinePlayerList_user%s' % userid, '_popup_choice', function) 
    offlinePlayerList.settitle('Select a player\nPage:')
    for steamid in wcs._dict_WCSPlayers:
        dictSteamid = wcs._dict_WCSPlayers[steamid]
        if not es.exists('userid', es.getuserid(steamid) ):
            offlinePlayers[steamid] = dictSteamid['name']
    """ Sort the dictionary to display by name in alphabetic order """
    sortedOfflinePlayers = sorted(offlinePlayers)
    if args:
        for steamid in sortedOfflinePlayers:
            offlinePlayerList.addoption([steamid, args], "%s (%s)" % (offlinePlayers[steamid], steamid ) )
    else:
        for steamid in offlinePlayers:
            offlinePlayerList.addoption(steamid, "%s (%s)" % (offlinePlayers[steamid], steamid ) )
    offlinePlayerList.submenu(10, 'wcs_offlineMenu')
    offlinePlayerList.cc_exitformat = "0. Back"
    offlinePlayerList.send(userid)
    
""" The following functions are functions shared by both online and offline menus... 
    they are the main 'backbone' to the admin menu, and will do the final actions """
    
def giveRaceLevels(userid, choice, popupid):
    """ This short function just sends the amount list to the player so they can select an amount to add """
    race, target = choice
    createAmountList(userid, target, commandGiveRaceLevels, race)
    
def commandGiveRaceLevels(userid = None, target = None, amount = None, race = None):
    """ This method gives the selected race additional levels, and also it initializes the event """
    if userid is None and target is None and amount is None and race is None:
        userid = es.getcmduserid()
        target = es.getargv(1)
        race   = es.getargv(2)
        amount = int(es.getargv(3))
    if isAdmin(userid):
        steamid    = getDictInstance(target)
        if steamid is not None:
            steamid[race]['level']          += amount
            steamid[race]['unspent skills'] += amount
            steamid['level']                += amount
            if isOnline(target):
                target = es.getuserid(target)
                es.event('initialize', 'wcs_levelup')
                es.event('setint',     'wcs_levelup', 'userid',   target)
                es.event('setint',     'wcs_levelup', 'amount',   amount)
                es.event('setint',     'wcs_levelup', 'oldlevel', steamid[race]['level'] - amount)
                es.event('setint',     'wcs_levelup', 'newlevel', steamid[race]['level'])
                es.event('setstring',  'wcs_levelup', 'race',   race)
                es.event('setstring',  'wcs_levelup', 'reason', "admin given")
                es.event('fire',       'wcs_levelup')
                es.tell(es.getuserid(target), '#multi', text('give level', {'race' : race, 'admin' : es.getplayername(userid), 'amount' : amount}, playerlib.getPlayer(userid).get("lang") ) )
        else:
            sendUnkownError(userid)
    
def giveXp(userid, choice, popupid):
    """ This short function just sends the amount list to the player so they can select an amount to add """
    createAmountList(userid, choice, commandGiveXp)

def commandGiveXp(userid = None, target = None, amount = None):
    """ Give the player some XP to his 'getxp' key so he can retrieve it at a later stage """
    if userid is None and target is None and amount is None:
        userid = es.getcmduserid()
        target = es.getargv(1)
        amount = int(es.getargv(2))
    if isAdmin(userid):
        steamid    = getDictInstance(target)
        if steamid is not None:
            if not 'getxp' in steamid:
                steamid['getxp'] = 0
            steamid['getxp'] += amount
        else:
            sendUnkownError(userid)

def giveLevels(userid, choice, popupid):
    """ This short function just sends the amount list to the player so they can select an amount to add """
    createAmountList(userid, choice, commandGiveLevels)

def commandGiveLevels(userid = None, target = None, amount = None):
    """ Give the player some XP to his 'getlevel' key so he can retrieve it at a later stage """
    if userid is None and target is None and amount is None:
        userid = es.getcmduserid()
        target = es.getargv(1)
        amount = int(es.getargv(2))
    if isAdmin(userid):
        steamid    = getDictInstance(target)
        if steamid is not None:
            if not 'getlevel' in steamid:
                steamid['getlevel'] = 0
            steamid['getlevel'] += amount
        else:
            sendUnkownError(userid)

def resetRaceLevel(userid, choice, popupid):
    """ Reset the race of the target which the admin selected. """
    race, target = choice
    steamid      = getDictInstance(target)
    if steamid is not None:
        for skill in wcs.Race(race).returnSkillList():
            steamid[race][skill] = 0
        currentLevel = steamid[race]['level']
        steamid[race]['xp']    = 0
        steamid[race]['level'] = 1
        steamid[race]['unspent skills'] = 0
        steamid[race]
        steamid['level'] -= currentLevel
        if isOnline(target):
            target = es.getuserid(target)
            es.tell(target, '#multi', text('race reset', {'race' : race, 'admin' : es.getplayername(userid)}, playerlib.getPlayer(target).get("lang") ) )
    else:
        sendUnkownError(userid)
    

def resetTotalLevel(userid, choice, popupid):
    """ Clear the players current dictionary values, and re-assign default values """
    steamid = getDictInstance(choice)
    if steamid is not None:
        steamid.clear()
        steamid['getlevel'] = float(es.ServerVar('wcs_freelevels'))
        steamid['getxp']    = 0
        wcs.defaultrace(userid, playerlib.uniqueid(userid, True))
        if isOnline(choice):
            """ Slay the player and tell them a message if they are online """
            choice = es.getuserid(choice)
            es.server.queuecmd('damage %s %s'% (userid, es.getplayerprop(userid, 'CBasePlayer.m_iHealth') ) )
            es.tell(choice, '#multi', text('reset total level', {'admin' : es.getplayername(userid)}, playerlib.getPlayer(choice).get("lang") ) )
    else:
        sendUnkownError(userid)
                
def retrieveXp(userid, choice, popupid):
    """ This short function just sends the amount list to the player so they can select an amount to give """
    race, target = choice
    createAmountList(userid, target, commandRetrieveXp, race)
    
def commandRetrieveXp(userid = None, target = None, amount = None, race = None):
    """ This method just assigns the amount of xp the player chose to the players race he designated. """
    if userid is None and target is None and amount is None and race is None:
        if es.getargc() <> 4:
            return
        userid = es.getcmduserid()
        target = es.getargv(1)
        race   = es.getargv(2)
        amount = int(es.getargv(3))
    steamid = getDictInstance(userid)
    if steamid is not None:
        currentAmount = steamid['getxp']
        if currentAmount:
            if amount > currentAmount: 
                amount = currentAmount
            steamid['getxp'] -= amount
            wcs.Command(userid).GiveXp(amount, text('used getxp', lang = playerlib.getPlayer(userid).get("lang") ), race)
    else:
        sendUnkownError(userid)

def retrieveLevels(userid, choice, popupid):
    """ This short function just sends the amount list to the player so they can select an amount to give """
    race, target = choice
    createAmountList(userid, target, commandRetrieveLevels, race)
    
def commandRetrieveLevels(userid = None, target = None, amount = None, race = None):
    """ This method just assigns the amount of levels the player chose to the players race he designated. """
    if userid is None and target is None and amount is None and race is None:
        if es.getargc() <> 4:
            return
        userid = es.getcmduserid()
        target = es.getargv(1)
        race   = es.getargv(2)
        amount = int(es.getargv(3))
    steamid = getDictInstance(userid)
    if steamid is not None:
        currentAmount = steamid['getlevel']
        if currentAmount:
            if amount > currentAmount: 
                amount = currentAmount
            steamid['getlevel'] -= amount
            wcs.Command(userid).GiveLevel(amount,  text('used getlevel', lang = playerlib.getPlayer(userid).get("lang") ), race)
    else:
        sendUnkownError(userid)
