# ./addons/eventscripts/_libs/python/esamlib.py

import base64
import es
import os.path
import StringIO
import sys
import urllib
import xml.etree.ElementTree as ElementTree

import psyco
psyco.full()

"""
Basic Library for interfacing with ESAM
"""

baseurl = 'http://addons.eventscripts.com/'

###

class DBGLog(object):
   def __init__(self):
      self.lines = []

   def write(self, text, dbglvl=None):
      if not dbglvl is None:
         es.dbgmsg(dbglvl, text)

      self.lines.append(text)

   def echo(self):
      for line in self.lines:
         es.dbgmsg(0, line)

dbglog = DBGLog()

###

class ESAMUpload(object):
   """
   Keyword arguments:
   # username - full username.
   # password - password in plain text.
   # basename - basename
   # filename - name of file.
   # filesize - size of file in bytes
   # file_extension - file extension (zip, py, or txt only)
   # version_notes - version notes.
   # version - file version
   # b64_file_content - binary file contents in base64 encoded.
   # addon_name - addon name
   # forum_url - full URL to forum thread (The THREAD not POST (requires ?t=XXXX url)
   # public_variable - public variable for script
   # addon_desc - addon description
   # es_version - minimal ES version required.
   # video_url - full URL to youtube video.
   # addon_summary - 255 character summary for addon.
   # ready4approval - 1/0 flag if addon is ready for approval
   # install_instructions - installation instructions
   # featurelist_url - full featurelist URL (leave blank if none)
   # es_load_args - es_load arguments (ie es_load mugmod or es_load /wcs/myrace/clown
   # status - status of addon in plain text (Hidden or Normal)
   """
   uploadurl = baseurl + 'sdk/upload'
   kw_reqs = ('username', 'password', 'basename', 'filename', 'filesize', 'file_extension', 'version_notes',
    'version', 'b64_file_content', 'addon_name', 'forum_url', 'public_variable', 'addon_desc', 'es_version',
    'video_url', 'addon_summary', 'ready4approval', 'install_instructions', 'featurelist_url', 'es_load_args', 'status')

   def upload(self, username, password, basename, path, addon_name, version, version_notes, addon_desc,
    addon_summary, es_load_args, es_version, forum_url='', public_variable='', video_url='', install_instructions='',
    featurelist_url='', ready4approval=False, status='Normal'):

      filesize = os.path.getsize(path)
      filename = path.replace('\\', '/').rsplit('/', 1)[1]
      file_extension = filename.rsplit('.', 1)[1]

      f = open(path, 'rb')
      b64_file_content = base64.encodestring(f.read())
      f.close()

      return self.kw_upload(username=username, password=password, basename=basename, filename=filename, filesize=filesize,
       file_extension=file_extension, version_notes=version_notes, version=version, b64_file_content=b64_file_content,
       addon_name=addon_name, forum_url=forum_url, public_variable=public_variable, addon_desc=addon_desc,
       es_version=es_version, video_url=video_url, addon_summary=addon_summary, ready4approval=int(ready4approval),
       install_instructions=install_instructions, featurelist_url=featurelist_url, es_load_args=es_load_args, status=status)

   def kw_upload(self, **kw):
      for item in self.kw_reqs:
         if item not in kw:
            kw[item] = ''

      u = urllib.urlopen(self.uploadurl, data=urllib.urlencode(kw))
      d = u.read()
      u.close()

      return d

esamupload = ESAMUpload()

def upload(*a, **kw):
   return esamupload.upload(*a, **kw)

###

class AddonQuery(object):
   searchurl = baseurl + 'sdk/search/%s/%s'
   updateurl = baseurl + 'sdk/updatedsince/'
   type_names = ('addon', 'name', 'desc', 'user', 'pvar', 'esmin')

   def search(self, searchtype, text):
      """
      Search types:
      # addon - Searchs multiple fields within an addon. (name, desc, etc)
      # name  - Addon Name
      # desc  - Addon Description
      # user  - All addons by a user
      # pvar  - All addons by a public variable
      # esmin - All addons by minimum ES version 
      """
      searchtype = str(searchtype).lower()
      if searchtype not in self.type_names: raise ValueError, "Invalid ESAM search type '%s'" % searchtype
      return self.getAddonList(self.searchurl % (text, searchtype))

   def updated_since(self, timestamp):
      return self.getAddonList(self.updateurl + '%i' % timestamp)

   def getAddonList(self, url):
      u = urllib.urlopen(url)
      data = ElementTree.XML(u.read())
      u.close()

      result = []
      for addon in data.findall('addon'):
         addon_data = {}
         for item in addon.getiterator():
            name = item.tag
            value = addon.findtext(name)
            if value:
               addon_data[name] = value

         if addon_data:
            result.append(addon_data)

      return result

aquery = AddonQuery()

def search(searchtype, text):
   return aquery.search(searchtype, text)

def updated_since(timestamp):
   return aquery.updated_since(timestamp)

###

import installlib # Don't move this

class ESAMAddon(installlib.Addon):
   """
      # A farily light "wrapper" around the ESAM SDK
        Also provides basic download method
   """
   baseurl = baseurl

   def __init__(self, xmldata, log=dbglog):
      self.data = xmldata
      self.log  = log

   def __getattr__(self, name):
      """
         # Get some addon attributes
            - id
            - author
            - authorid
            - name
            - basename
            - approved
            - es_load
            - date
            - woots
            - desc
            - summary
            - currentversion
            - versionnotes
            - video
            - defaultssid
      """
      return_val = self.get(name)

      if return_val is None:
         raise AttributeError, 'Addon %s has no \'%s\' attribute' % (self.basename, name)

      return return_val

   def getElements(self):
      return self.data.getiterator() if self.data else []

   def getElementNames(self):
      return map(lambda x: x.tag, self.getElements())

   def get(self, attribute):
      return self.data.findtext(attribute) if self.data else None

   def download(self):
      """
         # Returns a file instance of the addons latest downloadable version
      """
      if self.approved <> '1': return None

      try:
         u    = urllib.urlopen(self.baseurl + 'addons/download/' + self.basename)
         addon = StringIO.StringIO(u.read())
         u.close()

         return addon

      except IOError:
         info = sys.exc_info()
         self.log.write('ESAMLib: %s - %s' % (info[0].__name__, info[1]), 2)

      return None


def getAddon(basename, log=dbglog):
   try:
      u   = urllib.urlopen(baseurl + 'sdk/addoninfo/' + basename)
      xml = ElementTree.XML(u.read()).find('addon')
      u.close()

      return ESAMAddon(xml, log) if xml else None

   except IOError:
      info = sys.exc_info()
      log.write('ESAMLib: %s - %s' % (info[0].__name__, info[1]), 2)

   return None

###

class ESAMUser(object):
   class UserAddons(object):
      """
         # Class to deal with the users addons
      """
      def __init__(self, data, log=dbglog):
         self.data = data
         self.log  = log

         self.addons = {}

      def __getattr__(self, name):
         """
            # Provides a quick 'n' easy way to get addon attributes
            # Uses the basename and self.find to return an addon class
         """
         return_val = self.find(name)

         if return_val is None:
            raise AttributeError, 'UserAddons instance has no \'%s\' attribute' % name

         return return_val

      def find(self, addonname):
         """
            # Finds and returns an addon by its basename
         """
         if addonname in self.addons: return self.addons[addonname]

         for addon in self.getAddonList():
            if addonname == addon.findtext('basename'):
               return_addon = self.addons[addonname] = ESAMAddon(addon, self.log)
               return return_addon

         return None

      def getAddonList(self):
         return self.data.findall('addon')

      def getAddonNameList(self):
         return map(lambda x: x.findtext('basename'), self.getAddonList())

      def isAddon(self, basename):
         """
            # Returns true if the addon exists
         """
         return basename in self.getAddonNameList()

   """
      # Begin User class
      # A farily light "wrapper" around the ESAM SDK
        Also provides basic download method
   """

   def __init__(self, xmldata, addondata, log=dbglog):
      self.xmldata = xmldata
      self.addons  = self.UserAddons(addondata, log)

   def __getattr__(self, name):
      """
         # Allows you to get all the user data from the XML
         # The following attributes are available
            - username
            - userid
            - addontotal
            - addonrank
            - woottotal
            - wootrank
            - downloadtotal
            - downloadrank
      """
      return_val = self.get(name)

      if return_val is None:
         raise AttributeError, 'User instance with has no \'%s\' attribute' % name

      return return_val

   def getElements(self):
      return self.xmldata.getiterator() if self.xmldata else []

   def getElementNames(self):
      return filter(self.get, map(lambda x: x.tag, self.getElements()))

   def get(self, attribute):
      if not self.xmldata: return None

      return self.xmldata.findtext(attribute)


def getUser(username, log=dbglog):
   try:
      u   = urllib.urlopen(baseurl + 'sdk/getuserid/' + base64.b64encode(username))
      tid = u.read()
      u.close()

      if tid == "false" or not tid: return None

      u      = urllib.urlopen(baseurl + 'sdk/getuserinfo/' + tid)
      xmldata = ElementTree.XML(u.read())
      u.close()
      
      u       = urllib.urlopen(baseurl + 'sdk/getuseraddons/' + tid)
      addondata = ElementTree.XML(u.read())
      u.close()

      return ESAMUser(xmldata, addondata, log)

   except IOError:
      info = sys.exc_info()
      log.write('ESAMLib: %s - %s' % (info[0].__name__, info[1]), 2)

   return None

"""
>>> import esamlib

>>> sd = esamlib.getUser('SuperDave')

>>> sd.getElementNames()
['username', 'userid', 'addontotal', 'addonrank', 'woottotal', 'wootrank', 'downloadtotal', 'downloadrank']

>>> sd.username
'SuperDave'
>>> sd.userid
'11091'
>>> sd.downloadrank
'4'

>> sd.get('userid')
'11091'

>>> sd.addons.getAddonNameList()
['cheapbombtimer', 'regenmod', 'cheapzombie', 'cheapdeathmatch', 'gunmod', 'cheapwirecut', 'swapwithvictim', 'slomomod', 'slobot', 'knifegrenade', 'newrewards', 'cssoundtrack', 'cheapbleed', 'explosivedeaths', 'nadeslay', 'healmates', 'teamfog', 'healthregen', 'weaponstats', 'splashdamage', 'doublevip', 'cheapbeacon','nowyouseeme', 'coloredhealth', 'bomberman', 'sourceradar', 'ezrestrict', 'assistedkills', 'randmaps', 'iedmod', 'safespawn', 'ezfilter', 'extendedevents', 'iptocountry', 'cheapbulletwhizz', 'cheapemote']

>>> sd.addons.cssoundtrack
<esamlib.Addon object at 0x1173AB70>
>>> sd.addons.find('cssoundtrack')
<esamlib.Addon object at 0x1173AB70>

>>> sd.addons.cssoundtrack.getElementNames()
['addon', 'id', 'author', 'authorid', 'name', 'basename', 'approved', 'es_load', 'date', 'woots', 'desc', 'summary', 'currentversion', 'versionnotes', 'video','defaultssid']

>>> sd.addons.cssoundtrack.id
'13758'
>>> sd.addons.cssoundtrack.currentversion
'7'
>>> sd.addons.cssoundtrack.video
'http://www.youtube.com/v/G3S8l3BB4J0'
>>> sd.addons.cssoundtrack.woots
'21'
>>> sd.addons.cssoundtrack.date
'2007-05-02 04:21:26'
>>> sd.addons.cssoundtrack.defaultssid
'200'
"""

"""
>>> import esamlib

>>> css = esamlib.getAddon('cssoundtrack')

>>> css.getElementNames()
['addon', 'id', 'author', 'authorid', 'name', 'basename', 'approved', 'es_load', 'date', 'woots', 'desc', 'summary', 'currentversion', 'versionnotes', 'video','defaultssid']

>>> css.downloadAddon()
<StringIO.StringIO instance at 0x115BF738>

>>> css.currentversion
'7'
>>> css.approved
'1'

>>> css.get('approved')
'1'
"""