# -*- coding: latin-1 -*-

# Copyright (c) 2006 bruno schwander <bruno@tinkerbox.org> and
#                   Stas Zykiewicz <stasz@linux.isbeter.nl>
#
#           letterFlashcard.py
#
# 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 of the License, 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 Library 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, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

RCFILE = 0# Used to signal the core if we have a rc file
LF_DEBUG = 0# Use like this: if LF_DEBUG: print 'useful message'

EXTRABORDER = 10
# The locales we support, it's needed that we switch to English if we run
# in a non supported locale.
# Add any new locales to this list.
SUPPORTED_LOCALES = ['en','nl','fr']

import os,sys,random
import pygame
from pygame.constants import *

import utils
from utils import load_music,font2surf,MyError,trace_error
from SpriteUtils import CPSprite,CPGroup,CPinit
import Timer

from CPConstants import DATADIR,ASSETMLROOT,LIBDIR

from CPMenu import MenuItem

class Img:
    """ Container to store image objects"""
    pass
class Snd:
    """ Container to store sound objects"""
    pass
class Misc:
    """ Container to store all kind of stuff"""
    pass

class Button(MenuItem):
    def __init__(self,img,pos,data):
        MenuItem.__init__(self,img,pos,data)

class Card:
    def __init__(self,img,descr, article):
        self.img = img
        self.descr = descr
        self.article = article
        self.descrfontsize = 48
        self.initialfontsize = 300
        self.fcol = (226,178,31)
        self.ttf = os.path.join(DATADIR,'VeraSeBd.ttf')
        self.wordsurf,spam =  font2surf(self.descr,self.descrfontsize,self.fcol,self.ttf)
        self.initialsurf, spam = font2surf(self.descr[:1],self.initialfontsize,self.fcol,self.ttf)
        self.imgPos = self.img.get_rect()
        self.descrPos = self.wordsurf.get_rect()
        self.initialPos = self.initialsurf.get_rect()

    def addSound(self, sound):
        """ adds a sound object, the sound that that object does """
        self.sound = sound
        
    def addName(self, name):
        """ adds a sound object, the spoken name of the object """
        self.name = name

    def addInitialSound(self, initial):
        """ adds a sound object, the spoken initial of the object """
        self.initialSound = initial

    def getImg(self):
        return self.img

    def getSnd(self):
        return self.sound

    def setImagePos(self, x, y):
        self.imgPos.move_ip(x, y)

    def setDescrPos(self, x, y):
        self.descrPos.move_ip(x, y)

    def setInitialPos(self, x, y):
        self.initialPos.move_ip(x, y)
        
    def stopSound(self):
        """Needed to stop any sounds when the user quits"""
        try:
            self.sound.stop()
        except:
            pass

    def playSound(self):
        try:
            self.sound.play()
        except Exception,info:
            print "can't play card sound"
            print info
        
    def queueSound(self):
        try:
            self.sound.queue()
        except Exception,info:
            print "can't queue card sound"
            print info
        
    def playInitialSound(self):
        try:
            self.initialSound.play()
        except Exception,info:
            print "can't play card sound"
            print info
#to play that object as soon as the current one finishes. play() would stop current
    def queueInitialSound(self):
        self.initialSound.queue()

    def playName(self):
        try:
            self.name.play()
        except Exception,info:
            print "can't play card sound"
            print info
            
    def queueName(self):
        self.name.queue()

    def draw(self):
        rec = self.descrPos
        rec = Img.screen.blit(self.wordsurf,rec)
        rec2 = self.imgPos
        rec2 = Img.screen.blit(self.img,rec2)
        rec3 = self.initialPos
        rec3 = Img.screen.blit(self.initialsurf,rec3)

        pygame.display.update((rec,rec2, rec3))

    def erase(self):
        rec = self.descrPos
        rec = Img.screen.blit(Img.backgr,rec,rec)
        rec2 = self.imgPos
        rec2 = Img.screen.blit(Img.backgr,rec2,rec2)
        rec3 = self.initialPos
        rec3 = Img.screen.blit(Img.backgr,rec3,rec3)

        pygame.display.update((rec,rec2,rec3))

class Game(Img,Snd):    
    """  letterFlashcard - part of childsplay.py, a suite of educational games for
    young children. """
    def __init__(self,screen,backgr,rc_dic,basepath,libdir,cpg):
        self.screen = screen# SDL screen representing the visible screen
        self.backgr = backgr# A SDL surface which holds a copy of 'screen'

        Img.screen = self.screen# Add some 'global' references
        Img.backgr = self.backgr

        self.rc_dic = rc_dic# reference to the rcfile, if we have one, otherwise {}
        self.basedir  = basepath# path to the childsplay core directory
        self.libdir = libdir# path to the 'lib' directory which holds this file.
        self.cpg = cpg
        #~ self.Assets_snd = pyassetmlSDL.AssetmlSDL()
        #~ self.Assets_snd.set_mldir('childsplay/childsplay-sounds/childsplay-sounds.assetml')
        self.gamelevels =[1]# used by childsplay core (see childsplaytest.py)
        self.gameitems = [None]# used by childsplay core
        self.group = CPinit(self.screen,self.backgr)# Init SpritUtils stuff.
        self._setup()
       
    def __del__(self):
        """If you use the timer object you MUST stop it."""
        print "del called"
        try:
            self.timer.stop()
            self.card_array[self.word].stopSound()
        except:
            pass
            
    def updatebar(self):
        progress = self.pbar.update()
        pygame.display.update(self.screen.blit(self.pbar.get_bar(),(200,200)))      
    
    def _setup(self):
        """ Set all the stuff we need"""
        ## As this method sets up 70 cards with images and two sounds it takes
        ## a while to finishes. So we use a 'progressbar' to let the user know 
        ## we actually doing something.
        ## There are better ways to do the loading but this was design choosen so
        ## we have to live with it. (Stas Z)
        self.pbar = utils.ProgressBar(header=_("Loading cards, please wait"),\
                                    step=20,\
                                    ttf=os.path.join(DATADIR,'VeraSeBd.ttf'))
        self.updatebar()
        if LF_DEBUG: print "ChildsplayGoodies",dir(self.cpg)
        ## This is only needed when you decide to use assetml files.
        ## If you don't know what 'Assetml' is you probably don't need it :-)
        try:
            import pyassetml,pyassetmlSDL
        except (ImportError,MyError),info:
            print >> sys.stderr,info,"\nThis version of childsplay depends on pyassetmlSDL"
            text = "Module memory fails to import pyassetmlSDL"
            raise MyError,text

        loc = self.cpg.language
        if loc not in SUPPORTED_LOCALES:
            loc = 'en'
        if LF_DEBUG: print "letterFlashcard game, locale set to",loc
        
        # create two assetmlSDL instances one for parsing images and one for sounds
        self.Assets_Icons = pyassetmlSDL.AssetmlSDL()
        self.Assets_Icons.set_transparent_image(0)
        self.Assets_Icons.set_alpha_image(1)
        self.Assets_Icons.set_mldir('childsplay/childsplay-images/childsplay-images.assetml')

        butnFwdImg = self.Assets_Icons.get_assets(('arrow.png',))
        butnFwdImg = pygame.transform.rotate(butnFwdImg, 90)
        pos = (725,10)
        self.pageFwdButton = Button(butnFwdImg,pos,1)

        butnBwdImg = pygame.transform.rotate(butnFwdImg, 180)
        pos = (10,10)
        self.pageBwdButton = Button(butnBwdImg,pos,1)

        self.Assets_Img = pyassetmlSDL.AssetmlSDL()
        self.Assets_Img.set_transparent_image(0)
        self.Assets_Img.set_alpha_image(1)
        self.Assets_Img.set_mldir('childsplay/objectslib/pics/cards/cards.assetml')
        # {foo.png:foo.png object,spam.png:spam.png object}
        descr_pics = self.Assets_Img.get_assets(('*.png',),fullname=1)
        self.updatebar()
        picsParser = pyassetml.AssetmlParser('childsplay/objectslib/pics/cards/cards.assetml')
        screenW = self.screen.get_clip().width
        screenH = self.screen.get_clip().height

        initialsounddir = os.path.join(DATADIR,'AlphabetSounds',loc)
        namesounddir = os.path.join(ASSETMLROOT,'childsplay', 'objectslib', 'names',loc)
        crysounddir = os.path.join(ASSETMLROOT,'childsplay', 'objectslib','sounds')

        self.card_array = {}
        
        for k,v in descr_pics.items():
            found = picsParser.find_names((('file',k),('description',loc)),strict=1)
            if LF_DEBUG: 
                print "looking for",k
                print "found",found
            try:
                found = found[0]
            except (TypeError,IndexError),info:
                if LF_DEBUG: print >> sys.stderr,"Error in names searching, searchin for",k,"locale",loc
                found = None
            if found:
                if LF_DEBUG: print "Found localized cards images ",found
                if LF_DEBUG: print "descr_pics.item,v ", v
                if LF_DEBUG: print "descr_pics.item,k ", k
                card = Card(v,found.upper(), 'dummy')
                cardW = card.img.get_width()
                cardH = card.img.get_height()
                topMargin = card.descrfontsize + EXTRABORDER
                descrW = card.wordsurf.get_rect().width
                initialW = card.initialsurf.get_rect().width
                initialH = card.initialsurf.get_rect().height

                card.setImagePos(screenW/2 + screenW/4 - cardW/2,
                                 topMargin 
                                 + (screenH - topMargin)/2 
                                 - cardH/2) 
                card.setDescrPos( screenW/2 - descrW/2 , 5 )
                card.setInitialPos(screenW/4 - initialW/2, 
                                   topMargin 
                                        + (screenH - topMargin)/2
                                        - initialH/2) 
                # Here we add sounds to the cards.
                # The idea is that we always display a card regardless if there's a sound.
                sndobject = load_music(os.path.join(namesounddir, k[:-4]+ '.ogg'))
                if sndobject == 'NoneSound':
                    print "not found names soundfile",k[-4]
                    print "using nonesound object"
                card.addName(sndobject)
                
                sndobject = load_music(os.path.join(initialsounddir,found[:1].lower() + '.ogg'))
                if sndobject == 'NoneSound':
                    print "not found initial soundfile",found[:1]
                    print "using nonesound object"
                card.addInitialSound(sndobject)
                            
                sndobject = load_music(os.path.join(crysounddir,k[:-4] + '.ogg'))
                if sndobject == 'NoneSound':
                    print "not found animal soundfile",k[-4]+'.ogg'
                    print "using nonesound object"
                card.addSound(sndobject)

                self.card_array[found.upper()] = card
                
            else:
                if LF_DEBUG: print "Not Found card",k

        self.updatebar()
        if LF_DEBUG:
            print "len descr_pics",len(descr_pics)
            print "len self.card_array",len(self.card_array)
            print "card_array\n",self.card_array
        
        if LF_DEBUG: print "screen width, height: ",self.screen.get_clip().width

        if LF_DEBUG: print "after setImagePos:", card.imgPos

        self.updatebar()
        wrdlist = self.card_array.keys()
        
        self.wordlist = wrdlist[:]

        random.shuffle(self.wordlist)
        if LF_DEBUG: print "wordlist",self.wordlist
        # blit a background to make sure we cover the progressbar and any
        # other leftovers.
        bs = utils.load_image(os.path.join(self.libdir,'letterFlashcardData','back.jpg'))
        pygame.display.update(Img.screen.blit(bs,(0,0)))
        Img.backgr.blit(bs,(0,0))

    def __str__(self):
        """Must return the original, not translated, title of this game.
        It's needed by the high score class of childsplay."""        
        return "letterFlashcard"
        
    def _test(self):
        filea = 'test-alpha.png'        

        try:
            surfacea = pygame.image.load(filea)
        except pygame.error,info:
            TestAlphaError.line = 'load_image()'
            print >> sys.stderr, "testalpha, could not load",filea,info
            raise TestAlphaError, 'Could not load image "%s"\n %s'%(file, pygame.get_error())
    
        print "surfacea: ", surfacea
        #pursuant to pygame docs, the following is ignored since the
        #test image has alpha data
        surfacea.set_alpha(128, RLEACCEL) 
        #after experiment, it seems colorkey is ignored if the surface has alpha  
        #surfacea.set_colorkey(surfacea.get_at((0, 0)))
        #, RLEACCEL)
        rec2 = (370,10)
        rec2 = Img.screen.blit(surfacea,rec2)

        pygame.display.update((rec2))

    def start(self,levels,items):
        """This string is displayed when this game is started."""
        if LF_DEBUG: print "levels,items",levels,items
        
        self.pageFwdButton.display_sprite()
        self.pageBwdButton.display_sprite()

        self.wordIndex = 2
        word = self.wordlist[self.wordIndex]
        if LF_DEBUG: print "Choosen word",word
        
        self.word = word # use standard

        self._displayCard(self.word)
#       self._test()

    def _findNextInitial(self,c):

        e = self.wordlist[self.wordIndex+1:]
        b = self.wordlist[:self.wordIndex+1]

        e.extend(b)     
        self.wordlist = e
        self.wordIndex = 0

        for i,v in enumerate(self.wordlist):
            if v[:1] == c: 
                self.wordIndex = i
                self.word = v
                break

    def _displayCard(self, oldword):
        self.card_array[oldword].erase()
        self.card_array[self.word].draw()
        self.pageFwdButton.display_sprite()
        self.pageBwdButton.display_sprite()
        self.card_array[self.word].playName()
        self.card_array[self.word].queueSound()

    def _showNextCard(self):
        self.wordIndex += 1
        if self.wordIndex == len(self.wordlist):
            self.wordIndex = 0

        oldword = self.word
        self.word = self.wordlist[self.wordIndex]
        try:
            self._displayCard(oldword)
        except Exception,info:
            print "Exception in letterFlashcard"
            print info

    def _showPreviousCard(self):
        self.wordIndex -= 1
        if self.wordIndex == -1:
            self.wordIndex = len(self.wordlist)-1
        
        oldword = self.word
        self.word = self.wordlist[self.wordIndex]

        self._displayCard(oldword)


    def _check_key(self,key):
        key = unicode(key.upper())

        oldword = self.word
        self._findNextInitial(key)

        self._displayCard(oldword)

        return 0

    def _mouseDownProcess(self, event):
        if LF_DEBUG: print "event.pos:", event.pos
        
        pos = pygame.Rect(event.pos + (4,4))
        if not self.screen.get_clip().contains(pos):
            if LF_DEBUG: print "mouse outside game screen"
            return
            
        card = self.card_array[self.word]

        if card.imgPos.contains(pos):
            if LF_DEBUG: print "Hit the image!"
            card.playSound()

        if card.initialPos.contains(pos):
            if LF_DEBUG: print "Hit the initial!"
            card.playInitialSound()

        if card.descrPos.contains(pos):
            if LF_DEBUG: print "Hit the description!"
            card.playName()

        v = self.pageFwdButton.update(event)
        if v:
            if LF_DEBUG: print "pageFwdButton hit"
            self._showNextCard()

        v = self.pageBwdButton.update(event)
        if v:
            if LF_DEBUG: print "pageBwdButton hit"
            self._showPreviousCard()
 
    def helptitle(self):
        return _("LetterFlashcard")
    
    def help(self):
        text = [_("The aim of the game:"),
        _("This is a game to teach the alphabet to very little childrens."),
        _("At the start of the game, a photograph of an animal is shown, above the picture the name of the animal is written."),
        _("On the left the initial of the animal name is shown."),
        _("Then the animal name is spoken ('the dog'), and the animal makes his cry ('woof' 'cui-cui' etc.)."),
        _("When a letter is hit on the keyboard, a corresponding animal is shown."),
        _("Each part of the screen can also be clicked on to make the animal scream, his name or initial spoken."),
        " ",
        _("Difficulty : 2 - 4 years"),
        " ",
        _("Number of levels : 1")]
        return text

    def loop(self,events):
        """events is a copy of the current pygame event queue"""
        self.stop,Misc.score = 0,0
        ## this is called 40 times a second and should become your
        ## main eventloop. 
        item = None
        for event in events:
            if event.type is KEYDOWN and event.key == K_LEFT:
                self._showNextCard()

            if event.type is KEYDOWN and event.key == K_RIGHT:
                self._showPreviousCard()

                
            if event.type is KEYDOWN and 96<event.key<123:
                userkey = event.unicode
                #print userkey
                self._check_key(userkey[0])
                break
            if event.type is MOUSEBUTTONDOWN:
                if LF_DEBUG: print "event.pos:", event.pos
                self._mouseDownProcess(event)
                break
        # stop -> 0=continue, \
        #        2=user exit (hit the stopsign), \
        #       -1=end level and clear screen, \
        #        1=exit (game finished)\
        return self.stop,Misc.score
        
        
