import es
import playerlib
import popuplib
import gamethread

import psyco
psyco.full()

#plugin information
info = es.AddonInfo()
info.name = "Vote EventScripts python library"
info.version = "oy4c"
info.author = "Hunter"
info.url = "http://www.eventscripts.com/pages/Vote/"
info.description = "Provides vote handling for Source games"

#global variables:
## gVotes holds all the popups by their names for backwards compatibility
gVotes = {}
## gUsers holds user-specific information such as votes
gUsers = {}

# Get this library handle for registering events dynamically:
selfmodule = __import__('votelib')


## Vote_vote class is the vote base class
class Vote_vote(object):
    def __init__(self, pVoteid, pEndblock="", pSubblock=""):
        #initialization of vote
        self.name = pVoteid             #vote name for backwards compatibility
        self.running = False            #if the vote is running
        self.votes = 0                  #number of votes
        self.time = 0                   #time remaining
        self.question = ""              #vote question
        self.options = {}               #list of vote options
        self.endblock = pEndblock       #block that runs at the end of vote
        self.subblock = pSubblock       #block that runs for each submitted vote
        self.popup = None               #popup object
        #variables open to the public
        self.showmenu = True            #show the popup (set to False for e.g. HL2DM servers)
        self.endtime = True             #stop vote after the time is over
        self.enduser = True             #stop vote after all users voted
    def delete(self):
        #delete this vote, calls global delete function
        delete(self.name)
    def start(self, time=0):
        if not self.running:
            self.time = time
            self.running = True
            self.votes = 0
            self.popup = popuplib.easymenu("_vote_"+str(self.name), "_vote_choice", _submit)
            self.popup.voteid = self.name
            self.popup.c_titleformat = self.question + (" "*(30-len(self.question))) + " (%p/%t)"
            for option in self.options:
                self.popup.addoption(option, self.options[option].text)
            if self.showmenu:
                self.send(playerlib.getUseridList("#human"), True, False)
            else:
                es.dbgmsg(1, "Votelib: The vote '"+str(self.name)+"' is set to not show the menu")
            if self.time > 0:
                gamethread.delayed(1, _ticker, (self.name))
            else:
                self.endtime = False
        else:
            raise ValueError("Votelib: Cannot start vote %s, it is already running"%self.name)
    def stop(self, cancel=None, internal=None):
        if self.running:
            self.popup.unsend(playerlib.getUseridList("#human"))
            self.popup.delete()
            if cancel:
                es.set("_vote_name", str(self.name), "Name of the vote")
                es.set("_vote_id", "0", "ID of the winning answer")
                es.set("_vote_text", "0", "Text of the winning answer")
                es.set("_vote_count", "0", "Votes of the winning answer")
                es.set("_vote_percent", "0", "Percentage of the winning answer")
                es.set("_vote_votes", self.votes, "Total votes")
                es.set("_vote_tie", "0", "1 if there would be two or more winning answers and 0 if there is only one winning answer")
                es.set("_vote_canceled", "1", "1 if the vote was canceled and 0 if it was normaly stopped")
                if self.endblock != "":
                    es.dbgmsg(1, "Votelib: endblock='"+str(self.endblock)+"'"+str(type(self.endblock)))
                    if callable(self.endblock):
                        self.endblock(self.name, 0, "", 0, 0, self.votes, False, True)
                    else:
                        es.doblock(self.endblock)
            else:
                wID = 0
                wText = ""
                wCount = 0
                wPercentage = 0
                wTie = False
                votetext = ""
                for option in self.options: 
                    if int(self.options[option]) > wCount: 
                        wTie = False # Needed incase of a previous tie while it loops through 
                        wID = option 
                        wText = self.options[option].text 
                        wCount = self.options[option].submits 
                        if self.votes > 0: 
                            wPercentage = int((wCount * 100) / self.votes) #Fixed 
                        else: 
                            wPercentage = 100
                        try:
                            votetext = str(wText['en'])
                        except:
                            votetext = str(wText)
                    elif (int(self.options[option]) == wCount) and (wCount > 0): 
                        wTie = True
                es.set("_vote_name", str(self.name), "Name of the vote")
                es.set("_vote_id", wID, "ID of the winning answer")
                es.set("_vote_text", votetext, "Text of the winning answer")
                es.set("_vote_count", wCount, "Votes of the winning answer")
                es.set("_vote_percent", wPercentage, "Percentage of the winning answer")
                es.set("_vote_votes", self.votes, "Total votes")
                es.set("_vote_tie", int(wTie), "1 if there would be two or more winning answers and 0 if there is only one winning answer")
                es.set("_vote_canceled", "0", "1 if the vote was canceled and 0 if it was normaly stopped")
                if self.endblock != "":
                    es.dbgmsg(1, "Votelib: endblock='"+str(self.endblock)+"'"+str(type(self.endblock)))
                    if callable(self.endblock):
                        self.endblock(self.name, int(wID), str(wText), int(wCount), int(wPercentage), self.votes, wTie, False)
                    else:
                        es.doblock(self.endblock)
            self.running = False
            self.popup = None
            self.votes = 0
            self.time = 0
            for userid in gUsers:
                user = gUsers[userid]
                user.state[self.name] = False
                user.voted[self.name] = None
        else:
            raise ValueError("Votelib: Cannot stop vote %s, it is not running"%self.name)
    def send(self, pUsers, force=False, prio=False):
        if self.running:
            if self.showmenu:
                try:
                    # try if pUsers is just a single userid
                    userid = int(pUsers)
                    pUsers = [userid]
                except TypeError:
                    pass
                page = self.popup
                if page:
                    for userid in pUsers:
                        if not userid in gUsers:
                            gUsers[int(userid)] = Vote_user(int(userid))
                        user = gUsers[int(userid)]
                        if self.name not in user.state:
                                user.state[self.name] = False
                        if not user.state[self.name]:
                            es.dbgmsg(1, "Votelib: Send popup '"+str(page.name)+"' to user "+str(userid))
                            page.send(userid, prio)
                        elif force == True:
                            es.dbgmsg(1, "Votelib: Forced popup '"+str(page.name)+"' to user "+str(userid))
                            page.send(userid, prio)
            else:
                es.dbgmsg(1, "Votelib: The vote '"+str(self.name)+"' is set to not show the menu")
        else:
            raise ValueError("Votelib: Cannot send vote %s, it is not running"%self.name)
    def submit(self, pUsers, option, force=False):
        if self.running:
            try:
                # try if pUsers is just a single userid
                userid = int(pUsers)
                pUsers = [userid]
            except TypeError:
                pass
            page = self.popup
            if page:
                if (self.isValidOption(option)):
                    for userid in pUsers:
                        if not userid in gUsers:
                            gUsers[int(userid)] = Vote_user(int(userid))
                        user = gUsers[int(userid)]
                        if self.name not in user.state:
                            user.state[self.name] = False
                        if self.name not in user.voted:
                            user.voted[self.name] = None
                        if not user.state[self.name]:
                            self.options[option].add()
                            self.votes = self.votes + 1
                            user.state[self.name] = True
                            user.voted[self.name] = option
                            try:
                                votetext = str(self.options[option]['en'])
                            except:
                                votetext = str(self.options[option])
                            es.set("_vote_name", str(self.name), "Name of the vote")
                            es.set("_vote_id", int(self.options[option]), "ID of the submitted answer")
                            es.set("_vote_text", votetext, "Text of the submitted answer")
                            es.set("_vote_userid", userid, "UserID that submitted the answer")
                            if self.subblock != "":
                                es.dbgmsg(1, "Votelib: subblock='"+str(self.endblock)+"'"+str(type(self.endblock)))
                                if callable(self.subblock):
                                    self.subblock(userid, self.name, int(self.options[option]), str(self.options[option]))
                                else:
                                    es.doblock(self.subblock)
                        elif force == True:
                            if user.voted[self.name]:
                                self.options[user.voted[self.name]].sub
                            self.options[option].add()
                            user.state[self.name] = True
                            user.voted[self.name] = option
                            es.set("_vote_name", str(self.name), "Name of the vote")
                            es.set("_vote_id", int(self.options[option]), "ID of the submitted answer")
                            es.set("_vote_text", str(self.options[option]), "Text of the submitted answer")
                            es.set("_vote_userid", userid, "UserID that submitted the answer")
                            if self.subblock != "":
                                es.dbgmsg(1, "Votelib: subblock='"+str(self.endblock)+"'"+str(type(self.endblock)))
                                if callable(self.subblock):
                                    self.subblock(userid, self.name, int(self.options[option]), str(self.options[option]))
                                else:
                                    es.doblock(self.subblock)
                        es.dbgmsg(1, "Votelib: Submitted vote '"+str(self.name)+"' with option '"+str(self.options[option])+"' from user "+str(userid))
            else:
                es.dbgmsg(1, "Vote submit: The popup '"+str(self.popup.name)+"' does not exists")
            if self.enduser:
                if self.votes >= len(playerlib.getUseridList("#human")):
                    self.endtime = True
                    self.time = 1
            else:
                es.dbgmsg(1, "Votelib: The vote '"+str(self.name)+"' is set to not end after all users voted")
        else:
            raise ValueError("Votelib: Cannot submit vote %s, it is not running"%self.name)
    def information(self, listlevel):
        if listlevel >= 1:
            es.dbgmsg(0, " ")
        es.dbgmsg(0, self.name)
        if listlevel >= 1:
            es.dbgmsg(0, "  Running:      "+str(self.running))
            es.dbgmsg(0, "  Votes:        "+str(self.votes))
            es.dbgmsg(0, "  Timeleft:     "+str(self.time))
            es.dbgmsg(0, "  Question:     "+str(self.question))
            es.dbgmsg(0, "  Endblock:     "+str(self.endblock))
            es.dbgmsg(0, "  Subblock:     "+str(self.subblock))
            es.dbgmsg(0, "  Showmenu:     "+str(self.showmenu))
            es.dbgmsg(0, "  Endtime:      "+str(self.endtime))
            es.dbgmsg(0, "  Enduser:      "+str(self.enduser))
        if listlevel >= 2:
            es.dbgmsg(0, "  Options:      "+str(len(self.options)))
            for option in self.options:
                es.dbgmsg(0,"    ["+str(option)+"]:  "+self.options[option].text)
    def isValidOption(self, option):      #checks for valid options numbers
        if (option < 1): return False
        if (option > len(self.options)): return False
        return True
    def setquestion(self, text):          #set the question of the vote
        if not self.running:
            self.question = text
        else:
            raise ValueError("Votelib: Cannot setquestion vote %s, it is already running"%self.name)
    def setoption(self, option, text):    #add a line of text to the end of the list
        if not self.running:
            if (self.isValidOption(option)):
                self.options[option]=Vote_option(text, option)
            else:
                es.dbgmsg(0,"Votelib: Cannot modify option #"+str(option)+"("+text+") in "+self.name)
        else:
            raise ValueError("Votelib: Cannot setoption vote %s, it is already running"%self.name)
    def addoption(self, text):            #add a line of text to the end of the list
        if not self.running:
            index = len(self.options)+1
            self.options[index]=Vote_option(text)
        else:
            raise ValueError("Votelib: Cannot addoption vote %s, it is already running"%self.name)
    def deloption(self, option):          #delete an option
        if not self.running:
            if (self.isValidOption(option)):
                options = len(self.options)
                for thisoption in range(option,options):
                    self.options[thisoption] = self.options[thisoption+1]
                del self.options[options]
            else:
                raise IndexError("Votelib: Cannot delete option #%s, it does not eixsts"%option)
        else:
            raise ValueError("Votelib: Cannot deloption vote %s, it is already running"%self.name)
    def getvotes(self, option=None):
        if option:
            if (self.isValidOption(option)):
                return int(self.options[option])
            else:
                return None
        else:
            return self.votes
    def gettimeleft(self):
        return self.time
        

# Vote_option class
class Vote_option(object):
    def __init__(self, text):
        self.text = text
        self.submits = 0
    def __int__(self):
        return self.submits
    def __str__(self):
        return self.text
    def add(self):
        self.submits = self.submits + 1
    def sub(self):
        self.submits = self.submits - 1

# Vote user class
class Vote_user(object):
    def __init__(self, userid):
        self.userid = userid
        self.state = {}
        self.voted = {}

#vote commands begin here
#usage from other Python scripts for example:
#  import es
#  import vote
#  a = vote.create("insertnamehere")
def create(pVoteid, pEndblock="", pSubblock=""):
    #create new normal vote
    gVotes[pVoteid] = Vote_vote(pVoteid, pEndblock, pSubblock)
    return gVotes[pVoteid]

def delete(pVoteid):
    #delete a vote
    if (pVoteid in gVotes):
        del gVotes[pVoteid]
    else:
        raise ValueError("Votelib: Cannot delete vote %s, it does not exists"%pVoteid)

def exists(pVoteid):
    #does named vote exist
    return (pVoteid in gVotes)

def isrunning(pVoteid):
    #does named vote exist and is running (started)
    if (pVoteid in gVotes):
        return gVotes[pVoteid].running
    return False

def find(pVoteid):
    #return class instance of named popup
    if (pVoteid in gVotes):
        return gVotes[pVoteid]
    return None

def send(pVoteid, pUserid, force=False, prio=False):
    #send a named vote to user/users
    if pVoteid in gVotes:
        gVotes[pVoteid].send(pUserid,force,prio)
    else:
        raise ValueError("Votelib: Cannot send vote %s, it does not exists"%pVoteid)

###################
#Helper functions #
###################

def _ticker(voteid):
    vote = find(voteid)
    if vote:
        if vote.running:
            vote.time = int(vote.time) - 1
            if vote.time > 0:
                gamethread.delayed(1, _ticker, (vote.name))
            else:
                if vote.endtime:
                    vote.stop(False, True)
                else:
                    es.dbgmsg(1, "Votelib: The vote '"+str(vote.name)+"' is set to not end after timeout")

def _submit(userid, value, popupid):
    es.dbgmsg(1, "Votelib: _submit("+str(userid)+", "+str(value)+", "+str(popupid)+")")
    page = popuplib.find(popupid)
    if page.voteid in gVotes:
        gVotes[page.voteid].submit(userid, value, True)
    else:
        raise ValueError("Votelib: Invalid vote %s submitted from popup"%page.voteid)
                    
######################
#EventScripts events #
######################

def player_disconnect(event_var):
    #player gone, delete from memory
    if int(event_var['userid']) in gUsers:
        del gUsers[int(event_var['userid'])]
    

# Register for player events to be able to use custom events
es.addons.registerForEvent(selfmodule, 'player_disconnect', player_disconnect)
