
"""
     Copyright (C) 2002-2004 Stas Z <stas@linux.isbeter.nl>

 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, 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,
 675 Mass Ave, Cambridge, MA 02139, USA.

 This module provides some functions and classes for childsplay
 See the childsplay reference for more information
"""
import pygame,os,sys,fnmatch,traceback,operator,imp,glob,locale,\
        gettext,__builtin__,shutil
from pygame.constants import RLEACCEL,KEYDOWN,K_BACKSPACE,K_RETURN,\
                                MOUSEBUTTONDOWN,K_ESCAPE
from CPConstants import *
DEBUG = 0

class MyError(Exception):
    name = None
    line = None
    extra = None

def set_locale(lang=None):
    txt = ""
    print "Setting childsplay locale to ",lang
    try:
        if not lang or lang == 'system':
            try:
                lang = locale.getdefaultlocale()[0].split("_")[0]
            except ValueError:
                print >> sys.stderr,info
                lang = 'en'
        if lang == "en": 
            __builtin__.__dict__['_'] = lambda x:x
            txt = "Language set to %s" % lang
            return 'en'
        lang_trans = gettext.translation('childsplay',\
                                     localedir=LOCALEDIR,\
                                     languages=[lang])
        __builtin__.__dict__['_'] = lang_trans.ugettext
    except Exception,info:
        txt = "Cannot set language to %s \n switching to English" % lang
        print >> sys.stderr,info,txt
        __builtin__.__dict__['_'] = lambda x:x
        lang = 'en'
    return lang
    
def get_locale():
    """Return the current locale setting."""
    try:
        loc = locale.getdefaultlocale()[0]
    except ValueError:
        print >> sys.stderr,info
        loc = 'en'
    if loc == 'C' or loc == 'posix':
        # Default to English
        loc = 'en'
    return loc

def replace_rcfile():
    """This is only used when we want to replace an existing configfile."""
    #pass #enable this and comment out the rest when not replacing
    src = os.path.join(RCDIR,CHILDSPLAYRC)
    dst = os.path.join(HOMEDIR,'ConfigData',CHILDSPLAYRC) 
    if os.path.exists(src) and os.path.exists(dst):
        print "Replace childsplayrc file\n",src,"->\n",dst
        shutil.copyfile(src,dst)

class NoneSound:
    """Used by the load_sound and load_music functions to provide
     a bogus sound object.
     You can also test if your object is a NoneSound object or not.
     if sndobject == 'NoneSound' or if sndobject != 'NoneSound'."""
    def play(self,loop=None):
        pass
    def stop(self):
        pass
    def __eq__(self,other):
        if other == 'NoneSound': return True
        else: return False
    def __ne__(self,other):
        if other != 'NoneSound': return True
        else: return False

def load_sound(name):
    """Loads a sound -> pygame sound object.
      If no file can be loaded return a dummy class"""
                
    if not pygame.mixer or not pygame.mixer.get_init():
        print >> sys.stderr, 'Cannot load sound: %s \n%s\n pygame.mixer.error' % (name,pygame.get_error())
        
        print >> sys.stderr, 'Using Nonesound'
        return NoneSound()
    try:
        sound = pygame.mixer.Sound(name)
    except pygame.error, message:
        print >> sys.stderr, 'Cannot load sound: %s \n%s' % (name,pygame.get_error())
        print >> sys.stderr, 'Using Nonesound'
        return NoneSound()
    return sound

def load_music(file):
    """  Set up music object, if the music can't be loaded a bogus object will be returnt.
     Beware that due to SDL limitations you can only have one music source loaded.
     This means that even when you have multiple pygame instances, there can only be one
     music source. This function returns a filename wrapped in a sound like object with
     a play and stop method.
     For multiple sources use the pygame.Sound and wave combination.
     """
    class MusicObject:
        def __init__(self,filename):
            self.s = filename
        def play(self,loop=0):
            pygame.mixer.music.load(self.s)
            pygame.mixer.music.play(loop)
        def stop(self):
            pygame.mixer.music.stop()
	def queue(self):
	    pygame.mixer.music.queue(self.s)
    
    class NoneSound:
            def play(self,loop=0):
                pass
            def stop(self):
                pass
    if not pygame.mixer or not pygame.mixer.get_init():
        print >> sys.stderr, 'Cannot load sound, pygame.mixer.error', '\nUsing Nonesound'
        return NoneSound()
    try:
       playmusic = MusicObject(file)
    except (pygame.error,IOError),info:
        print >> sys.stderr,info,"\nDisable music"
        playmusic = NoneSound()
    return playmusic

def load_image(file, transparent=0, alpha=0):
    """loads an image and convert it"""
    try:
        surface = pygame.image.load(file)
    except pygame.error:
        trace_error()
        MyError.line = 'Utils.py -> load_image()'
        raise MyError, 'Could not load image "%s"\n %s'%(file, pygame.get_error())
    if transparent :
        surface.set_colorkey(surface.get_at((0, 0)), RLEACCEL)
    if alpha:
	if DEBUG: print "pyassetmlSDL alpha image",file
	#if the image loaded has alpha, the overall alpha set here
	#(128) will be overriden. Do it anyway for forcing RLEACCEL
	surface.set_alpha(128, RLEACCEL)
	# if the display is in a lower mode, then surface.convert would
	# convert the surface in an alpha-less surface.
	return surface

    return surface.convert()

def txt2surf(seq,x,y,fsize=18,title=None,box=None,fcol=(0,0,0),ttf=None):
    """Render a list of strings in a surface with the screen backgr -> pygame surface"""
    space = 16 #space at the left for the text
    offset = 8 # and top
    helpsurf = pygame.Surface((x,y)).convert()
    helpsurf.blit(pygame.display.get_surface(),(0,0))
    if box:
        r = pygame.draw.rect(helpsurf,(255,255,255),(8,8,x-16,y-16),2)
        helpsurf.set_clip(r.inflate(-16,-16))
    if title:
        surf,spam = font2surf(title,fsize+(fsize/3),fcol,ttf)
        helpsurf.blit(surf,((x-surf.get_width())/2,(box > 0)*space))
        offset += surf.get_height()+ 8
    try:
        font = pygame.font.Font(ttf,fsize)
    except RuntimeError,info:
        print >> sys.stderr,info,'\nUsing standard pygame font'
        font = pygame.font.Font(None,fsize)
    for line in seq:
        helpsurf.blit(font.render(line, 1, fcol),(space, offset))
        offset += fsize + 8
    return helpsurf

def char2surf(char,size,fcol=None,ttf=None):
    """renders a character to a surface.
    char must be a sting. Returns the surface."""
    if not fcol:
        fcol = (0,0,0)
    try:
        font = pygame.font.Font(ttf,size)
    except RuntimeError,info:
        print >> sys.stderr,info,'\nUsing standard pygame font'
        font = pygame.font.Font(None,size)
    try:
        s = font.render(char, 1, fcol)
    except Exception,info:
        print >> sys.stderr, "ERROR in font rendering\n",info
    return s

def font2surf(word,size,fcol=None,ttf=None,sizel=None):
    """Renders a text in a surface and returns a surface,size list of the items"""
    if not fcol:
        fcol = (0,0,0)
    try:
        font = pygame.font.Font(ttf,size)
    except RuntimeError,info:
        print >> sys.stderr,info,'\nUsing standard pygame font'
        font = pygame.font.Font(None,size)
    if sizel:
        sizelist = font.size(word)
    else:
        sizelist = map(font.size,word)
    surf = font.render(word, 1, fcol)
    return surf,sizelist

def get_files(basedir,match):
    """ Obsolete, use glob.glob.
     This will return the result of 
     glob.glob(os.path.join(basedir,match))
     """
    files = glob.glob(os.path.join(basedir,match))
    return files
    
class Dimmer:
    """
Dimmer class

Tobias Thelen (tthelen@uni-osnabrueck.de)
6 September 2001

PUBLIC DOMAIN
Use it in any way you want...

tested with: Pyton 2.0/pygame-1.1, Windows 98

A class for 'dimming' (i.e. darkening) the entire screen, useful for:
- indicating a 'paused' state
- drawing user's attention away from background to e.g. a Quit/Don't Quit
  dialog or a highscore list or...

Usage:

dim=Dimmer(keepalive=1)
  Creates a new Dimmer object,
  if keepalive is true, the object uses the same surface over and over again,
  blocking some memory, but that makes multiple undim() calls possible - 
  Dimmer can be 'abused' as a memory for screen contents this way..

dim.dim(darken_factor=64, color_filter=(0,0,0))
  Saves the current screen for later restorage and lays a filter over it -
  the default color_filter value (black) darkens the screen by blitting a 
  black surface with alpha=darken_factor over it.
  By using a different color, special effects are possible,
  darken_factor=0 just stores the screen and leaves it unchanged

dim.undim()
  restores the screen as it was visible before the last dim() call.
  If the object has been initialised with keepalive=0, this only works once.

"""
    def __init__(self, keepalive=0):
        self.keepalive=keepalive
        if self.keepalive:
            self.buffer=pygame.Surface(pygame.display.get_surface().get_size())
        else:
            self.buffer=None
        
    def dim(self, darken_factor=64, color_filter=(0,0,0)):
        if not self.keepalive:
            self.buffer=pygame.Surface(pygame.display.get_surface().get_size())
        self.buffer.blit(pygame.display.get_surface(),(0,0))
        if darken_factor>0:
            darken=pygame.Surface(pygame.display.get_surface().get_size())
            darken.fill(color_filter)
            darken.set_alpha(darken_factor)
            # safe old clipping rectangle...
            old_clip=pygame.display.get_surface().get_clip()
            # ..blit over entire screen...
            pygame.display.get_surface().blit(darken,(0,0))
            pygame.display.update()
            # ... and restore clipping
            pygame.display.get_surface().set_clip(old_clip)

    def undim(self):
        if self.buffer:
            pygame.display.get_surface().blit(self.buffer,(0,0))
            pygame.display.update()
            if not self.keepalive:
                self.buffer=None

def trace_error(file=sys.stderr):
    """  Print a stack trace usefull for debugging"""
    print >> file, '*'*60
    info = sys.exc_info()
    traceback.print_exception(info[0],info[1],info[2],file)
    print >> file,' Please send a bug report with this stuff to,\n childsplay@users.sourceforge.net'
    print >> file,'*'*60
    
class MazeGen:
    """  Perfect maze generator, based on the Mazeworks algorithm.(adapted for Packid)
  Constuctor takes two uneven integers eg rows,cols. If the rows/cols are even
  then they will be decreased by one.
  Usage: m = MazeGen(17,17)
         maze = m.get_maze()
  maze is a tuple with tuples representing a grid where a 0 stands for a wall,
  and 1 for a room. The outer walls are also zeros.
  """
              
    def __init__(self,rows,cols):
        rows = rows-(rows % 2 == 0)
        cols = cols-(cols % 2 == 0)
        self.matrix = []
        for r in range(rows):
            self.matrix.append(([0]* cols))
        self._make_maze()
    
    def _make_maze(self):
        import random
        cellstack = []
        maxrow = len(self.matrix)
        row = random.choice(range(1,maxrow,2))
        maxcol = len(self.matrix[0])
        col = random.choice(range(1,maxcol,2))
        #print 'start row,col',row,col
        maxcol -= 3
        maxrow -= 3
        currentcell = (row,col)
        self.matrix[row][col] = 1
        visit = 1
        nextcell = []
        while 1:
            nextcell = []
            #check neighbors
            if col < maxcol and self.matrix[row][col+2] == 0:
                nextcell.append(((row,col+1),(row,col+2)))
            if col > 2 and self.matrix[row][col-2] == 0:
                nextcell.append(((row,col-1),(row,col-2)))
            if row < maxrow and self.matrix[row+2][col] == 0:
                nextcell.append(((row+1,col),(row+2,col)))
            if row > 2 and self.matrix[row-2][col] == 0:
                nextcell.append(((row-1,col),(row-2,col)))
            
            if nextcell:
                next = random.choice(nextcell)
                # knock down the wall
                self.matrix[next[0][0]][next[0][1]] = 1
                self.matrix[next[1][0]][next[1][1]] = 1
                cellstack.append(((row,col)))# stack old cell
                row,col = next[1][0],next[1][1]
                
            else:# Backtrack our steps
                try:
                    row,col = cellstack.pop()
                except IndexError:
                    #print 'break'
                    break
        self.matrix[-2][-2] = 2
        self.matrix[-1][-2:] = [1,1]
        
    def get_maze(self):
        grid = tuple(map(tuple,self.matrix))
        return grid

def import_module(filename, globals=None, locals=None, fromlist=None):
    """ Import any module without changes to sys.path.
      Taken from the library reference.(Never invent the wheel twice)"""
    # Fast path: see if the module has already been imported.
    try:
        return sys.modules[filename]
    except KeyError:
        pass
    path, name = os.path.split(filename)
    name, ext = os.path.splitext(name)
    fp = None    
    try:
        fp, pathname, description = imp.find_module(name,[path])
        return imp.load_module(name, fp, pathname, description)
        if fp: fp.close()
    except:
        text = "Import of %s failed" % filename
        trace_error()
        raise MyError,text
        if fp: fp.close()
                
def entry_edit(entry="",maxlen=10,but_ok_rect=(0,0,0,0),but_cancel_rect=(0,0,0,0)):
    """ Basic entry field editing function. Returns a updated entry string.
      Starts his own event loop, and only breaks when some user event occurs.
      entry - string with the entry text ("" in the beginning)
      but_ok_rect - pygame rect from the (optional) ok-button.
      but_cancel_rect - same as but_ok for the cancel button.
      """
    stop = 0
    but_ok_rect = pygame.Rect(but_ok_rect)
    but_cancel_rect = pygame.Rect(but_ok_rect)
    while stop == 0:
        stop = 1 # we stop when we hit a 'break' else set stop to 0
        for event in pygame.event.get():
            if event.type is KEYDOWN:
                if 31 < event.key < 123 and len(entry) < maxlen: # printable chr
                    entry = "%s%c" % (entry,event.key)
                    break
                elif event.key is K_BACKSPACE and entry:
                    entry = entry[:-1]
                    break
                elif event.key is K_RETURN:
                    break
                elif event.key is K_ESCAPE:
                    entry = ""
                    break
                
            elif event.type is MOUSEBUTTONDOWN:
                pos = pygame.Rect(pygame.mouse.get_pos() + (4,4))
                if but_ok_rect.contains(pos):
                    break
                elif but_cancel_rect.contains(pos):
                    entry = ""
                    break
        else:
            stop = 0 # we don't hit break so we loop
        pygame.event.pump()
    return entry            
    

class ReturnObject:
    """ Class object to be used as the return object in the plugins eventloop.
     The xml_dic is used to send xml data to the controller. The xml dic and
     all the future attributes in this class gets only checkt by the controller when
     the stop attribute is true or int(3). In this case the controller only checks the xml_dic.
     
     Attributes:
         score = integer representing a score value to be added to the excisting score.
         stop = flag to signal the controller, 0=continue, 2=user exit (hit the stopsign),
                  -1=clear screen, 1=exit (game finished), 3=only check the xml_dic.
         xml_dic = dic used to parse data for the monitor. (see the docs or contact the mailinglist
                     for the preffered format).
     """
      
    def __init__(self,gamename=__name__):
        self.score = 0
        self.stop = 0
        ###### NOT IN USE YET, this is for the monitor plugin (Very alpha)#####
        ##self.xml_dic = {'col0':[], 'col1':[], 'col2':[], 'col3':[], 'pre':[]}
        
class Blink:
    """ A general class object to provide a 'blinking' image.
      Blink(img,img_h,pos,*args)
      img = image 1
      img_h = image 2 (the highlight version)
      pos = position to blit, tuple(x,y)
      *args = optional data gets returned in the DATA attribute
      The DATA attribute gets set by the instance which is selected
      by the mouse position.
    """
    DATA = None
    def __init__(self,img,img_h,pos,*args):
        self.img = img
        self.img_h = img_h
        self.image = self.img
        self.rect = self.image.get_rect().move(pos)
        self.data = args
        
    def update(self):
        """ Update the blink object -> the tuple (switched image,rect,data)""" 
        if self.rect.inflate(-10,-10).contains((pygame.mouse.get_pos()+(1,1))):
            self.image = self.img_h
            Blink.DATA = self.data
            return self.image,self.rect,self.data
        elif Blink.DATA is self.data:# Not selected by the mouse
            Blink.DATA = None # then unset DATA
            
        if self.image is self.img:
            self.image = self.img_h
        else:
            self.image = self.img
        return self.image,self.rect,None

def pick_one(scr,rects,field=None):
    """ pick_one(scr,rects,field=None) -> SDL surface.
     scr = main SDL screen.
     rects = list of displayed rects to choose from. 
     field = pygame.Rect of the area containing the rects.
     
     The rects are 'highlighted' when the mouse goes over them, sort of 'mouse-over-javascript'.
     When the user clicks a button the surface corresponding wiyh the current rect
     is returnt"""
    
    if not field:
        field = scr.get_rect()
    if len(rects) < 2:
        MyError.name = " Utils.pick_one: length of the rects list should be more than one.\n Not %s" % len(rects)
        raise MyError
        
    oldhot_spot = None
    iconsheet = pygame.display.get_surface().convert()# used as background to blit when the mouse moves
    while 1: #begin icon picking
        pygame.event.pump()
        events = pygame.event.get()
        for event in events:
            if event.type is KEYDOWN and event.key is K_ESCAPE:
                MyError.name = "Utils.pick_one(%s,%s,%s)" % (scr,rects,field)
                raise MyError
            pos = pygame.mouse.get_pos()+(4,4)
            if field.contains(pos):
                pos = pygame.Rect(pos)
                index = pos.collidelist(rects)
                if index == -1:# not found
                    break
                hot_spot = rects[index]
                if not oldhot_spot:
                    oldhot_spot = hot_spot
                if hot_spot != oldhot_spot:
                    scr.blit(iconsheet, oldhot_spot, oldhot_spot)
                    pygame.draw.rect(scr, (234,14,32), hot_spot.inflate(-1,-1), 2)
                    pygame.display.update((hot_spot, oldhot_spot))
                    oldhot_spot = hot_spot
                
                if event.type is MOUSEBUTTONDOWN:
                    return iconsheet.subsurface(hot_spot)


def __string_split(line, split):
    """ Split a string at the first space left from split index
      in two pieces of 'split' length -> two strings,
      where the first is of length 'split'.
      The second string is the the rest or None
      line = string
      split = integer
      """
    while line[split] != " " or split == 0:
        split -= 1
    line1 = line[:split]
    line2 = line[split+1:]
    
    return line1, line2

def txtfmt(text, split):
    """ Formats a list of strings in a list of strings of 'split' length.
       returns a new list of strings. This depends on utils.__string_split().
       text = list of strings
       split = integer
       """
    newtxt = []
    left, right = "", ""
    for line in text:
        while len(line) > split:
            left, right = __string_split(line,split)
            newtxt.append((left.rstrip()))
            line = right
        newtxt.append((line))
    txt = filter(None,newtxt)
    return txt

class ScaleImages:
    """ ImgObjects = sequence of SDL objects
      stdCardObj = None means no blitting on card, just return
                   the scaled images. Else the images are blitted on this object.
      TargetSize = desired scale size (tuple)
    """
    def __init__(self, imgObjects, TargetSize ,stdCardObj=None):
        self.TargetSize = pygame.Rect((0,0)+TargetSize)# convert this tuple in a pygame rect
                                                  # see: def _scale_if_needed
        if stdCardObj:
             # in case of error, we don't use a card back
             # just return scaled images. 
            border = 10 # border size of card (memory), 2 * border size
            try: 
                self.stdCard = stdCardObj
                # scale the card also to TargetSize, so we use one card for everything
                self.scaled_card = self._scale_if_needed(self.stdCard)
                self.TargetSize.inflate_ip(-border,-border)# reduce the size of the rect, so the images are smaller
                                                       # then the card (compensate for border)
            except:
                self.stdCard = None
        else:
            self.stdCard = None #to use as a test later, if we gonna blit the img on a card or not
                                # see get_images
         
        self.imgObjects = imgObjects
        
    def get_images(self):
        """ This returns a list with scaled images, blitted on a card if
         it was parsed to the class constructor.
        """
        imgs = map(self._scale_if_needed, self.imgObjects) #this returns always a surface, scaled or not
        if self.stdCard:# we have a blanc card to blit the images on
            card_imgs = []
            for img in imgs:
                card = self.scaled_card.convert() # convert() returns a copy, not a reference
                                              # this way we keep a 'clean' card
                # center the image on the card
                center_pos = img.get_rect()
                center_pos.center = self.TargetSize.center # Wow, magic, see the pygame reference on Rect
                                                    # a good understanding of the pygame.Rect is a real time saver.
                card.blit(img,center_pos)
                card_imgs.append((card))
            imgs = card_imgs[:]# a real copy, just to be sure :-)
        return imgs   
        
    def _scale_if_needed(self,img):
        """ We only scale down, not up because the result of upscaling sucks"""
         # Remember, TargetSize is a pygame rect (contains is a method of Rect)
        if not self.TargetSize.contains(pygame.Rect((0,0)+img.get_size())):
            return self._scale_card(img)
        else:
            return img  #not changed!
            
    def _scale_card(self,img):
        """ This does the actual scaling and returns a scaled SDL surface."""
        imgSize=img.get_size()
        
        # we assume TargetSize = x == y, a square
        # TODO XXX what if Target not a square ??
        if (imgSize[0] > imgSize[1]):# which one should we divide to get the longest side within the TargetSize 
            scale_ratio = float(imgSize[0])/self.TargetSize[2]# TargetSize is a rect -> (x,y,x-size,y-size)    
        else:
            scale_ratio = float(imgSize[1])/self.TargetSize[3]
                           
        scale_x = int(imgSize[0]/scale_ratio)
        scale_y = int(imgSize[1]/scale_ratio)
        if DEBUG:
            print "image size",imgSize
            print "scale ratio",scale_ratio
            print "scale x,y",scale_x,scale_y
        scaled_img = pygame.transform.scale(img, (scale_x,scale_y))
        return scaled_img
        
class GfxCursor:
    """
    Replaces the normal pygame cursor with any bitmap cursor.
    This is a nice little GfxCursor class that gives you arbitrary mousecursor
    loadable from all SDL_image supported filetypes. 

    Author: Raiser, Frank aka CrashChaos (crashchaos at gmx.net)
    Author: Shinners, Pete aka ShredWheat
    Version: 2001-12-15

    Usage:
    Instantiate the GfxCursor class. Either pass the correct parameters to
    the constructor or use setCursor, setHotspot and enable lateron.

    The blitting is pretty optimized, the testing code at the bottom of
    this script does a pretty thorough test of all the drawing cases.
    It enables and disables the cursor, as well as uses a changing background.

    In your mainloop, the cursor.show() should be what you draw last
    (unless you want objects on top of the cursor?). Then before drawing
    anything, be sure to call the hide(). You can likely call hide() immediately
    after the display.flip() or display.update().

    The show() method also returns a list of rectangles of what needs to be
    updated. You can also move the cursor with pygame.mouse.set_pos()


    That's it. Have fun with your new funky cursors.
    

    """

    def __init__(self,surface,cursor=None,hotspot=(0,0)):
        """
        surface = Global surface to draw on
        cursor  = surface of cursor (needs to be specified when enabled!)
        hotspot = the hotspot for your cursor
        """
        self.surface = surface
        self.enabled = 0
        self.cursor  = None
        self.hotspot = hotspot
        self.bg      = None
        self.offset  = 0,0
        self.old_pos = 0,0
        
        if cursor:
            self.setCursor(cursor,hotspot)
            self.enable()

    def enable(self):
        """
        Enable the GfxCursor (disable normal pygame cursor)
        """
        if not self.cursor or self.enabled: return
        pygame.mouse.set_visible(0)
        self.enabled = 1

    def disable(self):
        """
        Disable the GfxCursor (enable normal pygame cursor)
        """
        if self.enabled:
            self.hide()
            pygame.mouse.set_visible(1)
            self.enabled = 0

    def setCursor(self,cursor,hotspot=(0,0)):
        """
        Set a new cursor surface
        """
        if not cursor: return
        self.cursor = cursor
        self.hide()
        self.show()
        self.offset = 0,0
        self.bg = pygame.Surface(self.cursor.get_size())
        pos = self.old_pos[0]-self.offset[0],self.old_pos[1]-self.offset[1]
        self.bg.blit(self.surface,(0,0),
            (pos[0],pos[1],self.cursor.get_width(),self.cursor.get_height()))

        self.offset = hotspot

    def setHotspot(self,pos):
        """
        Set a new hotspot for the cursor
        """
        self.hide()
        self.offset = pos

    def hide(self):
        """
        Hide the cursor (useful for redraws)
        """
        if self.bg and self.enabled:
            return self.surface.blit(self.bg,
                (self.old_pos[0]-self.offset[0],self.old_pos[1]-self.offset[1]))

    def show(self):
        """
        Show the cursor again
        """
        if self.bg and self.enabled:
            pos = self.old_pos[0]-self.offset[0],self.old_pos[1]-self.offset[1]
            self.bg.blit(self.surface,(0,0),
                (pos[0],pos[1],self.cursor.get_width(),self.cursor.get_height()))
            return self.surface.blit(self.cursor,pos)

    def update(self,event):
        """
        Update the cursor with a MOUSEMOTION event
        """
        self.old_pos = event.pos

class ChildsplayGoodies:
    """Class to provide references to a number of objects, methods, functions etc.
    These references are put in here at runtime by the childsplay main module (childsplay.py)
    and can be used by the game modules to maniputlate some aspects of childsplay.
    The best way to query this object is to look at it from a game module."""
    language = "en"
