#~ /***************************************************************************
 #~ *            soundmemory.py
 #~ *
 #~ *  Fri Oct 22 08:54:33 2004
 #~ *  Copyright  2004  Stas
 #~ *  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; see the file COPYING.  If not, write to
# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
#

DEBUG = 0
RCFILE = 0
import os,sys,random
import pygame
from pygame.constants import *
from utils import load_image,MyError,import_module,trace_error
from SpriteUtils import CPSprite,CPinit

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

NOSOUNDTXT = """It seems that we cannot use the sound card right now and because this game
  is all about sound, we just quit.
  This problem can also be caused by another application which uses the
  soundcard right now."""

class SndBut(CPSprite):
    Selected = None # This is a global reference to hold a selected object
    def __init__(self,snd,pos):
        CPSprite.__init__(self)
        self.snd = snd
        # self.img and self.img_high are set in the Game class
        self.image = self.img.convert()# self.image and self.rect are mandatory for the pygame sprite class
        self.rect = self.image.get_rect().move(pos)
        self.high = 0 # used to signal highlight or not
        self.connect_callback(self.on_select_button, MOUSEBUTTONDOWN) # MOUSEBUTTONDOWN = pygame constant 
        
    def play(self):
        self.snd.play()
    
    def highlight(self):
        if self.high:# we use a flag, because the images used are references.
            self.image = self.img
            self.high = 0
        else:
            self.image = self.img_high
            self.high = 1
        r = Img.screen.blit(self.image,self.rect)
        pygame.display.update(r)
        
    def on_update(self,*args):
        """ This is called when there's no callback function registered with
         the connect_callback method."""
        pass # we use callbacks, this is just as an example
    
    def on_select_button(obj,event,*args):
        """ This is a callback function used by a CPSprite object, like the callback
        fuctions in gtk++.
        A callback function must be connected to the object with a call to the 
        connect_callback method.
        When this fuction is called by the class it is called with a tuple of arguments.
        This tuple consist of:
            0 - a reference to the connected object.
            1 - the event that triggers this call.
            2 .. - data which where passed to the connect call.
        """
        
        if DEBUG: print " obj,event,*args",obj,event,args
        
        if not SndBut.Selected: # nothing selected yet
            obj.play() # play the sound
            obj.highlight() # toggle the high light button
            SndBut.Selected = obj # store reference to compare when there's a second selection
            return
        if obj.rect is SndBut.Selected.rect:# selected the same object twice, do nothing
            return
        obj.play()
        obj.highlight()
        pygame.time.wait(1500)
        if SndBut.Selected.snd is obj.snd: # sounds are the same
            SndBut.Selected.remove_sprite()# remove sprite object
            obj.remove_sprite()# remove 
            SndBut.Selected = None
            Misc.score = 1
        else:
            SndBut.Selected.highlight() #toggle highlight to normal
            obj.highlight()
            SndBut.Selected = None

class Game:
    """  soundmemory.py - part of childsplay.py, a suite of educational games for
  young children.
  """
    def __init__(self,screen,backgr,rc_dic,basepath,libdir):
        self.screen = screen
        self.backgr = backgr
        back_img = load_image(os.path.join(libdir,'SoundMemory','but_back.png'))
        s = pygame.Surface((768, 468)).convert()
        for y in range(0,257, 256):
            for x in range(0, 513, 256):
                s.blit(back_img, (x,y)) 
        pygame.draw.rect(s, (0,0,0), s.get_rect(), 6)# draw a box
        self.s = s
        # store two surfaces for later use
        Img.screen = self.screen
        Img.backgr = self.backgr
        self.rc_dic = rc_dic
        self.basedir  = basepath
        self.libdir = libdir
        try:
            import pyassetmlSDL
            #assetmlSDL = import_module(os.path.join(self.basedir,'pyassetmlSDL.py'))
        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
        # create two assetmlSDL instances one for parsing memory sounds and one for CP sounds
        self.Assets_snd = pyassetmlSDL.AssetmlSDL()
        self.Assets_snd.set_mldir('childsplay/sounds-misc/sounds-misc.assetml')
        self.Assets_cpsnd = pyassetmlSDL.AssetmlSDL()
        self.Assets_cpsnd.set_mldir('childsplay/childsplay-sounds/childsplay-sounds.assetml')
        
        # number of items y, number of items y, x offset, y offset
        self.gamelevels = [(2,3,200,100),(2,4,180,100),(3,4,180,60),(4,6,50,20)]
        #self.gamelevels =[(3,4,90)]
        self.gameitems = [None]
        self.group = CPinit(self.screen,self.backgr)
        self._setup()
            
    def _setup(self):
        # Sound becomes self.Sound.<object of file>, self.Sound.<object of next file> ... etc
        self.Sound = Snd()# get a instance for passing to assetmlSDL
        self.Image = Img()
        files = (('*.ogg',))# sequence of one will return all the ogg files from assetml
        # See pyassetmlSDL.py get_assets docstring for this special sequence.
        
        d = self.Assets_snd.get_assets(files)
        try:
            objects = d.values()
        except AttributeError,info:
            print >> sys.stderr, "Can't find sounds",info
            raise MyError,info
        if DEBUG: print "dictionary from assetmlSDL",d,"/nvalues from dict ",objects
        if not pygame.mixer.get_init():
            MyError.name = str(self)
            MyError.extra = NOSOUNDTXT
            raise MyError
        if len(objects) < 12:
            print >> sys.stderr, "Not enough sounds found, must be at least 12"
            raise MyError,info
        self.snd_objects = objects
        
        if DEBUG: print "snd_objects",self.snd_objects
        # set images to class. We do this outside the class defenition because we
        # don't have a reference to the libdir until now.
        # And we use the same images for all the buttons.Also we check the sound objects for equality
        # not the images.
        SndBut.img = load_image(os.path.join(self.libdir,'SoundMemory','but_bleu_up.png'),1)
        SndBut.img_high = load_image(os.path.join(self.libdir,'SoundMemory','but_red_down.png'),1)
    
            
    def start(self,l,spam):
        """Hit the buttons and find pairs"""
        # stop and score are stored in class object to be used globally
        # restore screen
        self.screen.blit(self.s,(16,16))
        self.backgr.blit(self.s,(16,16))
        
        pygame.display.update()
        Misc.stop = 0
        Misc.score = 0
        r, c ,xoffset, yoffset = l
        x_offset =  SndBut.img.get_width()+ 16
        y_offset = SndBut.img.get_height() + 16
        # shuffle sounds
        random.shuffle(self.snd_objects)
        num = (r * c)/2
        objects = self.snd_objects[:num] * 2
        random.shuffle(objects)
        for y in range(r):
            for x in range(c): 
                obj = SndBut(objects.pop(),(xoffset + x*x_offset,yoffset + y*y_offset))
                # if we don't use callbacks you could do:
                # SndBut(self.snd_...) the instance adds itself to the group and everything in a group
                # gets rendered whit a call to <group>.draw() -> list of rects -> display.update(list)
                # (see pygame docs, sprite class)
                self.group.add(obj)        
        
    def loop(self,events):
        item,Misc.score = None,0
        for event in events:
            if event.type is MOUSEBUTTONDOWN:
                item = event
                break
        try:
            v = self.group.refresh(item)# Check all objects in group for callback function
            if v: print "return from refresh",v
        except:
            print trace_error()
            raise MyError
        
        # check if there objects left in the sprite group
        if not self.group.sprites():
            Misc.stop = -1
            Misc.score = 100
        if DEBUG and Misc.score:
            print "self.score",Misc.score
        return Misc.stop,Misc.score
    
    def __str__(self):
        """Must return the original, not translated, title of this game.
        It's needed by the high score class of childsplay."""        
        return "Soundmemory"

    def helptitle(self):
        return "Soundmemory"
    
    def help(self):
        text = [_("The aim of the game:"),
        _("Classic memory game where you have to find pairs of sounds."),
        " ",
        _("Difficulty : 2-5 years"),
        " ",
        _("Number of levels : 4")]

        return text
