#!/usr/local/bin/python2.7
# -*- coding: utf-8 -*-
"""
     Copyright (C) 2002-2006 Stas Z. <stas.zytkiewicz@gmail.com>

 This program is free software; you can redistribute it and/or
 modify it under the terms of version 2 of the GNU General Public License
 as published by the Free Software Foundation.  A copy of this license should
 be included in the file GPL-2.

 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; see the file GPL-2.  If not, write to
 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

 Childsplay - A suite of educational games for young children.

  This is beta software, please send bugs and/or comments to
  childsplay@users.sourceforge.net"""
  
CPDEBUG = 0
import sys,os
REDIRECT = 0

if len(sys.argv) > 1 and sys.argv[1] == '--version': CPDEBUG,REDIRECT = 0,0
if os.name == 'nt' and REDIRECT:
    try:
        import out
    except ImportError,info:
        print >> sys.stderr,info
    else:
        print >> sys.stderr,"Redirect sys.stderr"
        print "Redirect sys.stdout"
# Check for pygame
try:
    import pygame
    from pygame.constants import *
except ImportError,info:
    print >> sys.stderr,info,"\n\n Childsplay depends on pygame."
    print >> sys.stderr, "\n You must first install the pygame package.\n Due to the fact that this package has some tricky\n dependencies, you better install the one that came with your distribution."
    sys.exit(1)
## set a bigger buffer, seems that on win XP in conjuction with certain hardware
## the playback of sound is scrambled with the "normal" 1024 buffer.
###  XXXX this still sucks, signed or unsigned that's the question :-(
pygame.mixer.pre_init(22050, -16, 2 , 2048)
# Import childsplay modules
try:
    from utils import *
    import version
    import pyassetmlSDL
    from SpriteUtils import CPSprite,CPGroup,CPinit
    from CPMenu import CPMenu,MenuItem
    from CPConstants import *
    import pyassetmlSDL
except ImportError,info:
    trace_error()
    print >> sys.stderr, "Error importing childsplay module\n",info
    sys.exit(1)

## Global options, config dic. Look in def get_rc() for format ##
#__name__ = just to be consistent with a real config file
#fullscr = 1 #set to toggle fullscreen
rcdic_defaults = {'default':{'__name__':'default',\
                            'fullscr':1,\
                            'language':'system',\
                            'exclude_games':'',\
                            'letter_size':'normal',
                            'kiosk_mode':'no'}}

if CPDEBUG: print "sys.argv",sys.argv
if len(sys.argv) > 1:
    for item in sys.argv[1:]:
        if item == '--fullscreen':
            fullscr = 1
        elif item == '--window':
            fullscr = None
        elif item == '--no-splash':
            SPLASH = None
        elif item == '--kiosk-mode':
            kioskmode = 1
        elif item == '--version':
            print version.version
            raise SystemExit
        elif item == '--VERSION':
            print version.VERSION
            raise SystemExit
        elif item == '--help':
            print helptext
            raise SystemExit

if not CPDEBUG: print >> sys.stderr,__doc__
if SPLASH:
    # Start splash screen, runs in a seperate thread
    try:
        import splash
    except (Exception,pygame.error),info:
        print >> sys.stderr,info
        trace_error()
    splash_worker = splash.Worker()# starts it self
# Import other python stuff       
try:
    import random,fnmatch,gettext,locale,pickle,threading,shutil,exceptions
except ImportError, info:
    print >> sys.stderr, "Can\'t import module ",info
    trace_error()
    raise SystemExit
    
# Init pygame, we do this here AFTER the splash stuff which also init pygame
# in his own thread.
pygame.init()
if CPDEBUG: print "mixer settings on init",pygame.mixer.get_init()

tree = HOME_DIR_TREE.replace(';',os.sep)
for piece in tree.split(','):
    piece = os.path.join(HOMEDIR,piece)
    if not os.path.exists(piece):
        try:
            os.makedirs(piece)
        except OSError,info:
            print >> sys.stderr, "Message while creating directory tree in",os.environ["HOME"],"\n",info
# Put the childsplay rc file in the users home dir
HOME_RCDIR = os.path.join(HOMEDIR,'ConfigData')
src = os.path.join(RCDIR,CHILDSPLAYRC)
dest = os.path.join(HOME_RCDIR,CHILDSPLAYRC)
#print "homerc,src,dest",HOME_RCDIR,src,dest
if not os.path.exists(dest):
    try:
        shutil.copyfile(src,dest)
    except Exception,info:
        print >> sys.stderr, "Error in copy rc files"
        print >> sys.stderr, info

## check and get rc parser object
import ConfigParser
RCFILES = 1

def get_rc(file):
    """ Loads and pars a config file and puts it in a dic.
    This rc file is stripped of it's extension, if needed, to get a platform
    independant solution.
    The dic is a dic of dic's;
    {'default':{'__name__':'default','option1':'value'},'section':{'__name__'...}}.
    file = name of the module rcfile, without the path.
    """
    file,spam = os.path.splitext(file)
    rcobj = ConfigParser.SafeConfigParser()
    try:
        rcobj.read(os.path.join(HOME_RCDIR,file))
    except (ConfigParser.ParsingError,Exception):
        trace_error()
        try:
            rcobj.read(os.path.join(RCDIR,file))
        except (ConfigParser.ParsingError,Exception):
            trace_error()
        return None
    rcdic = {}
    for section in rcobj.sections():
        optdic = {}
        for option in rcobj.options(section):
            optdic[option] = rcobj.get(section,option)
            if CPDEBUG: print "section,option,value",section,option,optdic[option]
        rcdic[section] = optdic
    if CPDEBUG: print "rcdic.values()",rcdic.values()
    if rcdic and rcdic.has_key('default') and len(rcdic.keys()) > 1:# update sections with the default section  
        new_dic ={}
        dic_defaults = rcdic['default'].copy()
        del rcdic['default']
        if CPDEBUG: print 'dic old',rcdic
        for dic in rcdic.values():
            d = dic_defaults.copy()
            d.update(dic)
            #print 'd ',d
            new_dic[dic['__name__']] = d.copy()
        if CPDEBUG: print "new_dic ",new_dic
        return new_dic
    return rcdic
    
def set_globalrc(dic):
    """Here we check and make sure the rcdic is valid and also set some
    global constants to get easy access to the rc values"""
    global rcdic,fullscr,LANG,EXCLUDE_GAMES,kioskmode
    #print "rcdic",rcdic
    try:
        fullscr = int(rcdic['default']['fullscr'])
        LANG = rcdic['default']['language']
        EXCLUDE_GAMES = rcdic['default']['exclude_games']
        kioskmode = rcdic['default']['kiosk_mode']
    except (KeyError,ValueError,TypeError),info:
        print >> sys.stderr," Problems reading the childsplay config file,\n",\
                info,"\n using the defaults and replacing the corrupt rc file\n",\
                
        exc = sys.exc_info()[0]
        print exc
        if exc is exceptions.KeyError:
            replace_rcfile()
        rcdic = rcdic_defaults
        set_globalrc(rcdic) # read again
    
## look for childsplay rc file,XXX these options can be overruled by the commandline args.
if RCFILES:
    if CPDEBUG: print "looking for",os.path.join(HOMEDIR,'ConfigData',CHILDSPLAYRC)
    rcdic = get_rc(CHILDSPLAYRC)
else:
    rcdic = rcdic_defaults
set_globalrc(rcdic)

# Setup the ChildsplayGoodies object for the games
CPGoodies = ChildsplayGoodies()
# Add some rc variables to ChildsplayGoodies to share between modules
CPGoodies.kioskmode = kioskmode
# i18n, put a lot of exception caches because locale stuff keeps crashing
# on badly configured platforms. Mac OSX locale support is totally broken :-( 
try:
    STDLOC,LOCALE_RTL = set_locale(LANG)
except Exception,info:
    print >> sys.stderr,info
    CPGoodies.language = 'en'
    STDLOC,LOCALE_RTL = 'en',None
try:
    # Add support for the LANGUAGE env var.
    CPGoodies.language = STDLOC.split('@')[0].split('.')[0].split('_')[0]
except Exception,info:
    print >> sys.stderr,info
    CPGoodies.language = 'en'
    STDLOC,LOCALE_RTL = 'en',None
if LOCALE_RTL and pyfribidi and STDLOC in ['he','ar']:
    CPGoodies.locale_rtl = True
    CPGoodies.pyfribidi = pyfribidi


if CPDEBUG: 
    print "Childsplay locale set to %s" % STDLOC
    print "Bidirectional flag is set to %s" % LOCALE_RTL
## Overrule rc values, this sucks. This must be rewritten and cleaned up
## XXX we should get a nice and clean way to handle config files and commandline options
if len(sys.argv) > 1:
    for item in sys.argv[1:]:
        if item == '--fullscreen':
            fullscr = 1
        elif item == '--window':
            fullscr = None
#print "rcdic",rcdic

# check if we have menu icons for the found games
modules = []
# See set_globalrc. We test for entries there so when a user has a old config
# file without exclude_games, it will be replaced.
# Otherwise we break support for older versions.
#excludegames = rcdic['default']['exclude_games']
###### TODO packid is removed when running under RTL locale
###### It crashes :-(
#if LOCALE_RTL: print "The pacman game is disabled for your locale, it crashes :-("
for name in fnmatch.filter(os.listdir(MODULES),'*.py'):
    if CPDEBUG: print >> sys.stderr, "plugin",name
    if os.path.exists(os.path.join(DATADIR,'icons',name[:-3]+'.icon.png')):
        if name[:-3] in EXCLUDE_GAMES: #or name[:-3] == 'packid' and LOCALE_RTL:
            continue
        else:
            modules.append((name[:-3]))
    else:
        print >> sys.stderr,'Can\'t find menu icon for',name,'\nRemoving',name,'from modules list'
    if CPDEBUG: print "gamemodules",modules   
    
## setup a dic; key=menu icon pos., value = (module name, icon name).
## This dic is passed to the CPMenu constructor, in the main function.
menudic = {}
rows = (len(modules) > 6)+len(modules)/6
if rows == 0: rows += 1
y = 80
x = 80
modules_copy = modules[:]
#random.shuffle(modules_copy)
for y in range(80, 100*rows, 100):
    for x in range(80, 600, 100):
        try:
            name = modules_copy.pop()
        except IndexError:
            break
        menudic[(x,y)] = (name,name+'.icon.png',1)
if CPDEBUG: print >> sys.stderr,"Menu icons dic",menudic 

## Namespace containers, filled at run time
class Img:
    pass

class Snd:
    pass

class Misc:
    pass
    
# create two assetmlSDL instances one for parsing images and one for sounds
Assets_img = pyassetmlSDL.AssetmlSDL()
Assets_img.set_mldir('childsplay/childsplay-images/childsplay-images.assetml')

Assets_snd = pyassetmlSDL.AssetmlSDL()
Assets_snd.set_mldir('childsplay/childsplay-sounds/childsplay-sounds.assetml')   

############################################
########### End toplevel code #####################
############################################

class Firework:
    """ Class used to display the fireworks at game-over time."""
    def __init__(self,x,letter):
        self.sndboom = Snd.boom
        self.fireR = Img.firework1
        self.fireL = Img.firework2
        self.img = self.fireR
        self.letter = letter
        self.x = x
        self.y = 600
        self.speed = 15
        self.height = 100
        self.rect = self.fireL.get_rect()
        self.rect.move_ip(self.x,self.y)
    
    def update(self):
        """ To be called from the eventloop, when the height is reached the img attr. is
          changed to the letter attr. This way the eventloop is displaying the letter"""
        if self.y <= self.height:
            self.y = 600
            Snd.rocket.stop()
            self.sndboom.play()
            self.img = self.letter
            return 1
        else:
            self._move()
        return 0
    
    def _move(self):
        """ Move the rocket to the top of your monitor.
          Each time switching between two images to give the impression of fire"""
        self.y -= self.speed
        self.rect.move_ip(0,-self.speed)
        if self.img is self.fireR:
            self.img = self.fireL
        else:
            self.img = self.fireR
      
class ScoreThing:
    """ Provides a score surface, the thing you see to the right."""
    def __init__(self,fsize=12,fcol=(0,0,0)):
        self.fsize = fsize
        self.fcol = fcol
        surf_s,s_size = font2surf('Score:',self.fsize,self.fcol,None,1)
        self.score_pos = (12,4+s_size[1])
        surf_size = (s_size[0]+12,s_size[1]*2+4)
        self.base_surf = pygame.Surface(surf_size).convert()
        pygame.draw.rect(self.base_surf,(0,255,0),self.base_surf.get_rect(),4)
        self.base_surf.blit(surf_s,(6,4))       
    
    def update(self,score='0000'):
        """ Called from the controller when there's a score to add. 
          Returns a updated sdl surface."""
        surf = self.base_surf.convert()
        sc = ('0000'+score)[-4:]# left fill with zero's
        surf_s,s_size = font2surf(sc,self.fsize,self.fcol,None,1)
        surf.blit(surf_s,self.score_pos)
        return surf
        
class Score:
    """ High score stuff, takes care of everything that's high-score related.
     It uses a 'scoresheet' which is a dic with lists as values and the game names
      as keys and is stored on disk as a 'pickle object' to get a cheap encrytion."""
    def __init__(self):
        self.hsc_ttf = os.path.join(DATADIR,'Domestic_Manners.ttf')
        self.sc_ttf = os.path.join(DATADIR,'VeraSeBd.ttf')
        self.sc_fsize = 32
        self.sc_fcol = (7,249,23)
        
        self.bal_l_img = Assets_img.get_assets(('balloonsl.png',))# This way get_assets returns one object
        self.bal_r_img = Assets_img.get_assets(('balloonsr.png',))
        try:
            f = open(SCOREFILE,'r')
            self.score_sheet = pickle.load(f)
            f.close()
        except (EOFError,IOError),info:
            trace_error()
            print >> sys.stderr,'Something went wrong with the score file\n Trying to create a new one'
            self.score_sheet = self._make_default_sheet()
            try:
                self._put_sheet()
                print >> sys.stderr, 'OK'
            except MyError:
                print >> sys.stderr,'Problems with the scoring class, using bogus scoring',\
                info,'\n',MyError.name,MyError.line,'\nPlease send a bug report with the above stuff to\n childsplay@users.sourceforge.net'

    
    def _make_default_sheet(self):
        """ Used if there's no score file found, this generaly means that someone deleted the
         score file. (why should you delete it anyway?).
         You can run childsplay as root and this will create a new score file.
         SECURITY-WARNING, childsplay should __never__ be run as root, so just start childsplay
         and stop it, the score file would be created at startup."""
         
        sheet = {}
        templ = []
        for name in ['Tux','Linus','Guido','RMS','Dijkstra']:
            templ.append(((100,name)))
        templ.sort()
        for name in modules:
            sheet[name.capitalize()] = templ[:]
        return sheet

    def _fetch_score(self):
        """ Load the high score file, makes a default score sheet when the load fails."""
        try:
            f = open(SCOREFILE,'r')
            score_sheet = pickle.load(f)
            f.close()
        except (IOError,EOFError):
            trace_error()
            print >> sys.stderr,'Error in score class, don\'t use it','Using bogus sheet'
            return self._make_default_sheet()
        return score_sheet
            
    def _put_sheet(self):
        """ Saves the high-score to disk, prints errors when it fails but continue anyway"""
        try:
            f = open(SCOREFILE,'w')
            pickle.dump(self.score_sheet,f)
            f.close()
        except IOError,info:
            trace_error()
            print >> sys.stderr,'Error in writing score'
            MyError.name = 'Error in writing score'
            MyError.line = info
    
    def get_score(self,name):
        """ First get the file from disk (_fetch_score), then get the
         list representing the high-score for this game (name)
         If there's no entry returns a messages, otherwise return
         the list."""
        self.score_sheet = self._fetch_score()
        #print >> sys.stderr,"score name",name
        #print >> sys.stderr,"score sheet",self.score_sheet
        try:
            sheet = self.score_sheet[name]
        except KeyError:
            return [(0,'Not possible to score points here')]
        sheet.sort()
        sheet.reverse() #list items -> (score,name)
        return sheet

    def check_high_score(self,name,score):
        """ Check if the score is a high-score. If true set high-score flag and start _high-score""" 
        sheet = self.get_score(name)
        if not self.score_sheet.has_key(name):
            return
        flag = None # Not in use, yet
        if score >= sheet[-1][0]:
            flag = 1
            if len(self.score_sheet[name]) >= 8:
                self.score_sheet[name] = self.score_sheet[name][:7]
                self._put_sheet()
            self.score_sheet[name] = self._high_score(name,score)
            self._put_sheet()
        self.clear_scr()
        return flag #trigger game over screen or not. Not in use, yet

    def _high_score(self,name,score):
        """ This starts the whole high-score event, usses it's own eventloop and
         takes care of everything"""
        # TODO Make the RTL thing work
        print "highscore locale",LOCALE_RTL,STDLOC
        sheet = self.get_score(name)
        self.sdim.dim(darken_factor=255,color_filter=(0,0,0))
        temp_back = pygame.Surface((800,500))
        temp_back.blit(self.screen,(0,0),(0,0,800,500))
        #self._score_eyecandy(temp_back)
        pygame.display.update(self.screen.blit(self.bal_l_img,(0,0)))
        pygame.display.update(self.screen.blit(self.bal_r_img,(665,0)))
        
        Assets_snd.get_assets(('Winner2.ogg',)).play(1)
        
        max_x = self.screen.get_width()# used to make RTL stuff screen size independant
        txt = _("Congratulations,")
        surf,spam = font2surf(txt,48,self.sc_fcol,self.hsc_ttf)
        x = 250
        if LOCALE_RTL: x = max_x-surf.get_width()-x
        y=20
        pygame.display.update(self.screen.blit(surf,(x,y)))
        
        txt = _("You are a winner")
        surf,spam = font2surf(txt,32,self.sc_fcol,self.hsc_ttf)
        x = 300
        if LOCALE_RTL: x = max_x-surf.get_width()-x
        y=120
        pygame.display.update(self.screen.blit(surf,(x,y)))
        
        txt = str(score)+_(" Points !!")
        surf,spam = font2surf(txt,48,self.sc_fcol,self.hsc_ttf)
        x = 160
        if LOCALE_RTL: x = max_x-surf.get_width()-x
        y=200
        pygame.display.update(self.screen.blit(surf,(x,y)))

        # This is the new RTL aware entry box
        entry = EntryEdit(self,self.screen,(80,400),\
                        _("Please give your name: "),\
                        loc=(LOCALE_RTL,STDLOC))
        text = entry.ask()

        #print text
        sheet.append(((score,text)))
        sheet.sort()
        sheet.reverse()
        return sheet
        
class Contr(Score):
    """ The main controller of the program is in charge of everything, besides some things
     that are in the childsplay.main(). (please look there also).
     When a game is started by the main function control is turnt over to this controller
     Now i'm become the borg-queen, the controller of worlds :-)"""
     
    def __init__(self):
                       
        ## Set icon for the window manager ##    
        file = os.path.join(DATADIR,'logo_cp_32x32.xpm')
        try:
            surface = pygame.image.load(file)
        except pygame.error:
            trace_error()
            print >> sys.stderr,'Could not load image "%s"\n %s'%(file, pygame.get_error())
        surface.set_colorkey(surface.get_at((0, 0)), RLEACCEL)
        pygame.display.set_icon(surface)
        self.tuxplace = 20
        self.tuxrects = []
        #self.mainmenurects = []
        self.setup()
        # fire up the score stuff
        Score.__init__(self)
        self.score = 0
                    
    def setup(self):
        x = 800
        y = 600
        self.y = y
        self.x = x
        #print "setup fullscreen>",fullscr
        if fullscr:
            self.screen = pygame.display.set_mode((x, y),FULLSCREEN)
        else:
            self.screen = pygame.display.set_mode((x, y))
        self.sdim = Dimmer(keepalive=0)
        self.clock = pygame.time.Clock()
        title = _('Childsplay, educational games for young children')
        try:
            pygame.display.set_caption(title.encode('ISO-8859-15'))
        except UnicodeEncodeError,info:
            print >> sys.stderr, info
        self.backcol = (80,151,244)
        self.ttf = os.path.join(DATADIR,'Domestic_Manners.ttf')
        self.backgr = pygame.Surface((x,y))
        Assets_img.set_transparent_image(0)
        Assets_img.set_alpha_image(1)
        files = ('land.png','stop.png','question1.png','hscore.png','tux.png',\
                'OK_but.png','MORE_but.png','firework1.png','firework2.png')
        Assets_img.get_assets(files,Img)
        if CPDEBUG: print "Img.__dict__",Img.__dict__
        Assets_img.set_alpha_image(0)
        Assets_img.set_transparent_image(0)
        Img.blue = Assets_img.get_assets(('blue.png',)) 
        Assets_img.set_transparent_image(1)
        
        self.backgr.blit(Img.blue, (0,0))
        self.backgr.blit(Img.land, (0,y-86))
        
        # setup the 'icons' from the bottom bar
        CPinit(self.screen,self.backgr)# because MenuItem derives from CPSprite
        self.mainmenu_objects = []
        objects_data = {'stop':(Img.stop,(690,y-95)),\
                        'questionmark':(Img.question1,(32,y-96)),\
                        'score':(Img.hscore,(120,y-85))}           
        for k,v in objects_data.items():
            img,pos = v
            obj = MenuItem(img,pos,k)
            self.backgr.blit(obj.image,obj.rect)
            self.mainmenu_objects.append(obj)
                
        Img.black = pygame.Surface(Img.firework1.get_size()).convert()
        
        if LOCALE_RTL:
            ST_size = 16
        else:
            ST_size = 24
        self.scorething = ScoreThing(ST_size,(0,255,0))
        self.scorething_place = (600,y-60)
        self.backgr.blit(self.scorething.update(),self.scorething_place)
        
        self.screen.blit(self.backgr, (0, 0))
        Img.screen = self.screen
        
        pygame.display.update()

        files = ('level_clear.wav','boom.wav','rocket.wav')
        Assets_snd.get_assets(files,Snd)  
        
    def mainloop(self,obj):
        """ This is the main eventloop (M.O.A.L - mother of all loops),from this loop the game loop
         is called. The M.O.A.L. gets the events list, calls the game loop, monitors the return values
         and takes appropiate actions."""
        
        stop,score = 0,0
        
        # stop and score flags
        # stop -> 0=continue, \
        # 2=user exit (hit the stopsign), \
        # -1=end level and clear screen, \
        # 1=exit (game finished)\
        # 3 = restart current level
        while stop == 0:
            score = 0
            self.clock.tick(40)
            pygame.event.pump()
            events = pygame.event.get()

            ### call game loop ##
            stop,score = obj.loop(events[:])
            ## checks for return values
            if score:
                #print 'c.mainloop stop',stop,'score',score
                self.update_scorething(score)
                
            for event in events:
                if event.type is QUIT:
                    self.ask_exit()
                elif event.type is KEYDOWN and event.key is K_ESCAPE:
                    self.ask_exit()
                # THIS IS ONLY FOR TAKING SCREENSHOTS
                # IT SHOULD BE COMMENT OUT FOR RELEASE
                #~ elif event.type is KEYDOWN and event.key == K_F1:
                    #~ try:
                        #~ scr = pygame.display.get_surface()
                        #~ print scr
                        #~ path = os.path.join('/tmp',str(obj)+'.BMP')
                        #~ pygame.image.save(scr,path)
                    #~ except RuntimeError,info:
                        #~ print >> sys.stderr, info
                # END OF SCREENSHOT STUFF
                elif event.type is MOUSEBUTTONDOWN:
                    for item in self.mainmenu_objects:
                        v = item.update(event)
                        if v:
                            if v == 'questionmark':
                                stop = self.get_help(obj,16)
                            elif v == 'score' and obj.__class__.__name__ == 'Game':
                                stop = self.show_score(Misc.helptitle)
                            elif v == 'stop':
                                if obj.__class__.__name__ == 'CPMenu':
                                    stop = self.ask_exit()# if return 0 then no exit   
                                else:
                                    stop = 2
        # self.score gets updated by the display_scorething method            
        return stop,self.score # let the main function decide what next
    
    def ask_exit(self):
        """ Really quit ?, the key to hit is also i18n. The strings are translated
          in the toplevel code because we can get here from within a game.
          look in the toplevel for remarks."""
        oldclip = self.screen.get_clip()
        self.screen.set_clip()
        text = (_("Do you really want to quit ?"),_("Hit (Y)es or (N)o"))
        dirtyrects = []
        self.sdim.dim(darken_factor=180,color_filter=(51,10,10))
        #sdim.dim(darken_factor=200,color_filter=(9,247,19)) # Used for testing
        # TODO: place holder for kioskmode stuff
        x= 100
        y= 200
        fcol = (255,0,0)
        #fcol = (224,109,67) # Used for testing
        for line in text:
            surf,size = font2surf(line,48,fcol,self.ttf)
            if LOCALE_RTL:
                l = surf.get_width()
                dirtyrects.append((self.screen.blit(\
                                    surf,(self.screen.get_width()-l-x,y))))
            else:
                dirtyrects.append((self.screen.blit(surf,(x,y))))
            y += surf.get_height() + 50
        pygame.display.update(dirtyrects)
        clock = pygame.time.Clock() 
        stop = 0
        key_map = None
        if LOCALE_RTL and STDLOC:
            key_map = {'he':he_key_map,
                        'ar':ar_key_map}[STDLOC]
        if STDLOC == 'ru': 
            key_map = ru_key_map
        while stop == 0:
            clock.tick(30)
            #events = pygame.event.get()
            event = pygame.event.poll()
            if kioskmode:
                pass
                # placeholder: setup virtual keyboard
                # should return the users choice which can be checked in _check_ask_exit_value
                # the virt keyb should destroy itself when the user hits enter.
            if event.type == KEYDOWN:
                k = 0
                m = pygame.key.get_mods()
                # when using key_maps : < > and ? need special care (keys 59,47,46,44)
                if LOCALE_RTL and event.key == 59 or event.key == 47 or \
                    event.key == 46 or event.key == 44:
                    k = event.key
                elif m & KMOD_SHIFT:
                    k = event.key-32
                else:
                    k = event.key
                if not 31 < k < 127:
                    continue
                user_key = ord(map_keys(key_map,chr(k)))
                if not self._check_ask_exit_value(user_key):
                    break
                stop = 1
                self.sdim.dim(darken_factor=255,color_filter=(0,0,0))
                surf,size = font2surf(_("Shutting down....."),72,(207,55,48),self.ttf)
                Snd.mainmusic.stop()
                pygame.display.update(self.screen.blit(surf,(100,200)))
                threads = threading.enumerate()
                if len(threads) > 1:
                    for t in threads[1:]:
                        try:
                            t._stop()# if it's the timer object
                        except Exception,info:
                            print info
                            del(t)                    
                sys.exit(0)
        self.sdim.undim()
        self.screen.set_clip(oldclip)
        return 0
    
    def _check_ask_exit_value(self,user_key):
        """This is a separate method so that we can use it in kiosk mode also
        This will return 0 for 'no' or 1 for 'yes' in answer to the 'really quit'
        question."""
        if STDLOC in ['he','ar','ru']:
            endkey = ord(_("y"))
        else:
            endkey = ord(_("y").lower())
        if user_key != endkey:
            v = 0
        else:
            v = 1
        return v       

    def update_scorething(self,score):
        """ Update and blit the score display to the main screen"""
        if score < 0: return
        if score > 0:
            self.score += score
        if CPDEBUG: print "update score",self.score,"with",score
        self.display_scorething()
    
    def display_scorething(self):
        score = str(self.score)
        self.screen.set_clip()
        self.backgr.set_clip()
        surf = self.scorething.update(score)
        pygame.display.update(self.screen.blit(surf,self.scorething_place))
        self.backgr.blit(surf,self.scorething_place)
        self.screen.set_clip(0,0,800,500)
        self.backgr.set_clip(0,0,800,500)

    def show_score(self,name):
        """ Display a hall-of-fame, this uses methods from the Score class and the help method.
        This is called when the user hits the hall-of-fame icon"""
        class spam:
            """ Bogus class, the help method expect a class obj"""
            def __init__(self,name,txt):
                self.name = name
                self.txt = txt
            def helptitle(self):
                return self.name
            def help(self):
                return self.txt
        txt = []
        i = 1
        for item in self.get_score(name):
            txt.append(("               %s  %s%s%s" % (i,item[1]," - ",item[0])))
            i += 1
        obj = spam(name,txt)
        self.get_help(obj,20)
        return 0
    
    def clear_scr(self,backgr=None):
        self.screen.blit(screendim.buffer,(0,0))
        if backgr:
            self.backgr.blit(screendim.buffer,(0,0))
        screendim.undim()
    
    def place_tux(self):
        """ Place a tux when a level is cleared, the wait is needed to let the music finish"""
        Snd.level_clear.play()
        screendim.undim()
        for r in self.tuxrects:
            pygame.display.update(self.screen.blit(Img.tux, r))
        self.tuxplace = self.tuxplace+ 80
        r = self.screen.blit(Img.tux, (120 + self.tuxplace,self.y-86))
        self.backgr.blit(Img.tux, (120 + self.tuxplace,self.y-86))
        self.tuxrects.append((r))
        pygame.display.update(r)
       
        pygame.time.wait(1500)
    
    def get_help(self,obj,fsize=24):
        """ Display the help text for the current game/plugin. It's using it's own eventloop.
          The text must be provided by the plugin."""
        if CPDEBUG: print "Starting get_help"
        if not obj.help():
            return 0
        ## cols and maxlines must be changed in conjuction with the fontsize 
        cols = 74
        maxlines = 15
        
        helptxt = txtfmt(obj.help(), cols)
        
        helptitle = obj.helptitle()
        oldclip = self.screen.get_clip()
        self.screen.set_clip()
        ttf = os.path.join(DATADIR,'VeraSeBd.ttf')
        lines = len(helptxt)
        if CPDEBUG: print "get_help lines",lines
        for i in range(0, lines, maxlines):
            self.sdim.undim()
            self.sdim.dim(darken_factor=200,color_filter=(51,10,10))
            update_rects = []
            part = helptxt[i:i+maxlines]
            surf = txt2surf(part,self.x-20,self.y-104,fsize,helptitle,1,(158,196,107),ttf)
            update_rects.append((self.screen.blit(surf,(0,0))))
            if i+maxlines < lines: #more to come
                okrect = self.screen.blit(Img.OK_but,(self.x-700,self.y-90))
                update_rects.append((okrect))
                morerect = self.screen.blit(Img.MORE_but,(self.x-400,self.y-90))
                update_rects.append((morerect))
            else:
                okrect = self.screen.blit(Img.OK_but,(self.x-500,self.y-90))
                update_rects.append((okrect))
                morerect = None
            pygame.display.update((update_rects))
        
            # go in eventloop and wait for user action        
            stop = 0
            while stop == 0:
                events = pygame.event.get()
                for event in events:
                    if event.type is QUIT:
                        self.ask_exit()
                    elif event.type is KEYDOWN and event.key is K_ESCAPE:
                        self.ask_exit()
                    
                    if event.type is MOUSEBUTTONDOWN:
                        pos = pygame.Rect(pygame.mouse.get_pos() + (4,4))
                        if okrect.contains(pos):
                            self.sdim.undim()
                            self.screen.set_clip(oldclip)
                            return 0
                        elif morerect:
                            if morerect.contains(pos):
                                stop = 1
                                break
        return 0
        
    def game_over(self):
        """ The game-over screen, restores the screen to the begin situation.
        It also displays some eyecandy."""
        # reset counters for placing tux
        self.tuxplace = 0
        self.tuxrects = []
        gameoverobj = []
        x = 20
        for item in 'GAME OVER':# can be any text
            try:
                font = pygame.font.Font(self.hsc_ttf,124)
            except Exception,info:
                print >> sys.stderr,info,'\nUsing standard pygame font'
                font = pygame.font.Font(None,96)
            surf = font.render(item, 1, (209,249,9))
            if item == " ": # we don't use spaces
                x = x + 48
                continue
            gameoverobj.append((Firework(x,surf)))
            #x = x + size[0][1]-48
            x = x + surf.get_width()
        
        self.sdim.dim(darken_factor=255,color_filter=(0,0,0))
        for obj in gameoverobj:
            Snd.rocket.play()
            stop = 0
            while stop == 0:
                self.clock.tick(40)
                r = self.screen.blit(Img.black,obj.rect)
                stop = obj.update()
                rr = self.screen.blit(obj.img,obj.rect)
                pygame.display.update((r,rr))
            pygame.time.wait(500)
        pygame.time.wait(1000)
        self.sdim.undim()
        self.clear_scr(1)     

def main():
    """ Main function responsable for loading and calling the start methods of the games,
     and after some init stuff turns control over to the Controller (class Contr)"""
    global screendim,game_score
    
    c  = Contr()# Start master control
    CPGoodies.scorething = c.update_scorething   
    Assets_snd.get_assets('music1.ogg', Snd)# will become Snd.music1
    Snd.galaga = load_sound(os.path.join(DATADIR,'Galaga1.ogg'))
    Snd.dub = load_sound(os.path.join(DATADIR,'dub.ogg'))
    Snd.thanks = load_sound(os.path.join(DATADIR,'thanks.ogg'))
    music_collection = [Snd.music1,Snd.galaga,Snd.dub,Snd.thanks]
    
    screendim = Dimmer(keepalive=1) # dimmer is used for screen copying
    screendim.dim(darken_factor=0) # just stores a copy in Dimmer.buffer
    screen  = c.screen
    back = c.backgr
    menu = CPMenu(screen,back,menudic,BASEDIR)
    while 1:
        Snd.mainmusic = random.choice(music_collection)
        Snd.mainmusic.play(-1)
        menu.start()
        plugin_name,spam = c.mainloop(menu)
        if CPDEBUG: print >> sys.stderr,"menu choice",plugin_name   
        c.screen.blit(screendim.buffer,(0,0))
        c.backgr.blit(screendim.buffer,(0,0))
        screendim.undim()
        Snd.mainmusic.stop()
        stop = None
        try:
            # import game module
            game_module = import_module(os.path.join(LIBDIR,plugin_name+'.py'))
            if CPDEBUG: print "imported module:",game_module
            try:
                if game_module.RCFILE:# can we use a rcfile?
                    rcfile = plugin_name+'rc'
                    src = os.path.join(RCDIR,rcfile)
                    dest = os.path.join(HOME_RCDIR,rcfile)
                    if not os.path.exists(dest) and os.path.exists(src):
                        shutil.copyfile(src,dest)
                    plug_rcdic = get_rc(rcfile)
                else:
                    plug_rcdic = {}
            except Exception,info:
                print >> sys.stderr,"Error while reading/copy rc file.\n",info
                trace_error()
                plug_rcdic = {}
                
            obj = game_module.Game
            c.screen.set_clip(0,0,800,500)
            c.backgr.set_clip(0,0,800,500)
            
            game = apply(obj,(c.screen,c.backgr,\
                        plug_rcdic,BASEDIR,MODULESDATADIR,\
                        CPGoodies))# call game constructor
            
            Misc.helptitle = str(game)# we need the org name for the score sheet
                                    
            c.screen.set_clip()
            c.backgr.set_clip()
            
            print game.__doc__
            print game.start.__doc__
                        
            levels = game.gamelevels
            items = game.gameitems
            game_score = 0
            c.score = 0
            # stop and score flags
            # stop and score flags
            # stop -> 0=continue, \
            # 2=user exit (hit the stopsign), \
            # -1=end level and clear screen, \
            # 1=exit (game finished)\
            # 3 = restart current level, not in use yet
            for l in levels:
                c.display_scorething()
                for i in items: 
                    #print 'i',i
                    stop = 0
                    c.screen.set_clip(0,0,800,500)
                    c.backgr.set_clip(0,0,800,500)
                    game.start(l,i)
                    stop,score = c.mainloop(game)
                    
                    if stop == -1:
                        stop = 0
                        c.screen.blit(screendim.buffer,(0,0))
                        c.backgr.blit(screendim.buffer,(0,0))
                        screendim.undim()
                        
                    elif stop == 2 or stop == 1:
                        try:
                            game.__del__()
                        except:
                            pass
                        del(game)
                        # reset counters for placing tux
                        c.tuxplace = 20
                        c.tuxrects = []
                        c.screen.set_clip()
                        c.backgr.set_clip()
                        break
                    c.screen.set_clip()
                    c.backgr.set_clip()
                else:
                    c.place_tux()
                
                if stop == 2 or stop == 1:
                    break
        except (MyError,Exception),info:
            trace_error()
            print >> sys.stderr,'********************** ERROR *********************\n',\
                    ' A error occured in module',MyError.name,' LineNo:',MyError.line,'\n',\
                    ' Info:',MyError.extra,'\n',\
                    ' The original error message is:\n',info,\
                    '\n**************************************************'
            stop = 2
        try:
            game.__del__()
        except:
            pass
        if stop != 2:
            if c.score:
                c.check_high_score(Misc.helptitle,c.score)
                c.score = 0
            c.game_over()
        
        c.screen.set_clip()
        c.backgr.set_clip()
        c.screen.blit(screendim.buffer,(0,0))
        c.backgr.blit(screendim.buffer,(0,0))
        screendim.undim()
        
        
if __name__ == '__main__':

    if SPLASH:
        # stop splash
        try:
           splash_worker.stop_splash()
        except Exception,info:
            print info
    try:
        main()
    finally:
        pass

