# -*- coding: utf-8 -*-

# Copyright (c) 2007 Stas Zykiewicz <stas.zytkiewicz@gmail.com>
#
#           fallingletters.py
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 3 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-3.
#
# 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.
# -*- coding: latin-1 -*-


## This is a port of the gletters (falling letters) activity from gcompris.

RCFILE = 1
DEBUG = 0

import os,sys,random
import pygame
from pygame.constants import *
from utils import load_image,trace_error,char2surf,MyError
from SpriteUtils import CPSprite,CPGroup,CPinit
import Timer

class Img:
    pass
class Snd:
    pass
class Misc:
    pass

class Letter(CPSprite):# we derive from this high-level class
    def __init__(self,c,pic,x,step,delay):
        CPSprite.__init__(self)# embed it in this class
        self.image= pic
        self.letter = c
        start = (x,0)
        self.delay = delay
        self.wait = delay
        self.rect = self.image.get_rect()
        end = (x,500+pic.get_height())# move of the screen
        loop = 0# 0 means one time, 1 means twice
        self.set_movement(start,end,step,0,loop)# setup the movement of this object
        self.connect_callback(self.callback, event_type=KEYDOWN)
                
    def __str__(self):
        return self.letter
    
    def on_update(self,*args):
        """This is called by group.refresh method"""
        if self.wait:# extra delay, otherwise they drop still to fast 
            self.wait -= 1
            return
        else:
            self.wait = self.delay
            status = self.moveit()# we move accoording to the movement set in the constructor
            if status != -1:# reached the end of the movement
                self.remove_sprite()
                # call the fail_to_type_letter?

    def callback(self,*args):
        """ This is called when the sprite class update method is called.
        Remember that you must 'connect' a callback first with a call to
        self.connect_callback, see the CPSprite reference for info on possibilities
        of connecting callbacks .
        """
        if DEBUG: print __name__,"args>",args[0],args[1]
        try:
            c = args[1].unicode.upper()
        except ValueError:
            return
        if c == str(self):
            self.remove_sprite()
            return 2

class Level:
    def __init__(self):
        """All these attributes will get their value at run time"""
        self.timersleep = 0# set the timerobject delay
        self.delay = 0# set a extra pause in the speed of the letter object
        self.step = 0 # set the number of pixels the letter will fall
        self.background = None # name of the background png 
        
class Game(Img,Snd):    
    """  Falling letters - part of childsplay.py, a suite of educational games for
  young children. """
    def __init__(self,screen,backgr,configdic,basepath,libdir,cpg):
        self.screen = Img.screen = screen
        self.backgr = Img.backgr = backgr
        gamelevels = []
        for level in ('0','1','2'):
            l = Level()
            l.timersleep = {'0':6, '1':5, '2':4}[level]
            l.delay = {'0':6, '1':4, '2':2}[level]
            l.step = {'0':1, '1':1, '2':2}[level]
            l.background = {'0':'scenery0.png',\
                            '1':'scenery1.png',\
                            '2':'scenery2.png'}[level]
            gamelevels.append(l)
        self.gamelevels = gamelevels
        self.gameitems = (1,2)
        
        self.letters = map(chr,range(65,91))
        print configdic
        size = 56
        try:
            self.configdic = configdic['default']
            size = {'normal':56,'medium':86,'large':116}[self.configdic['letter_size']]
        except Exception,info:
            print info
        print "Setting the fonts size to %s" % size
        self.fontsize = size
        self.basedir  = basepath
        self.libdir = libdir
        
        self.stop = 0
        self.score = 0
        # You MUST call CPinit BEFORE using any of the SpriteUtils stuff
        # it returns a reference to the special CPGroup
        Misc.actives = CPinit(self.screen,self.backgr)
        self._setup()

    def __del__(self):
        try:
            self.timer.stop()
        except:
            pass
            
    def _setup(self):
        """ Set all the stuff we need"""
        self.timer = None # will hold the timer object
        
    def start(self,level,items):
        """Try to hit the falling letters on the keyboard before they hit the ground."""
        if DEBUG: print "level object", level.__dict__
        self.score, self.stop = 0, 0
        if self.timer: self.timer.stop()
        Img.org_backgr = Img.backgr.convert() # save a copy of the original, just in case
        s = load_image(os.path.join(self.libdir,'FallingLettersData',level.background),0)
        
        # restore scenery
        Img.screen.blit(s, (0, 0))
        Img.backgr.blit(s,(0,0))
        pygame.display.update()
        # setup the letters
        self.letter_objects = []
        random.shuffle(self.letters)
        for c in self.letters:
            img = char2surf(c,self.fontsize,(123,3,127))
            x = random.randint(10,740)
            self.letter_objects.append(Letter(c,img,x,level.step,level.delay))
        # special threaded timer object
        self.timer = Timer.Timer(level.timersleep,self.drop_letter)
        self.timer.start()
        
    def drop_letter(self):
        """Called by the timer, and responsible for the letter dropping.
        This will get a letter from the letter_objects list and place it in
        the Sprite actives group, and thereby it will be blitted on the screen."""
        try:
            obj = self.letter_objects.pop()
        except IndexError:
            return 0
        Misc.actives.add(obj)
        return 1        
    
    def loop(self,events):
        item,self.score = None,0
        for event in events:
            if event.type is KEYDOWN:
                item=event
                break
        try:
            v = Misc.actives.refresh(item)# this erase the sprites, calls their update method
                                   # and redraws them including a call to pygame.display.update
            # check the return val from a callback or on_update method
            # for the format see the reference
            if v:
                self.score = v[0][1]
        except:
            print trace_error()
            raise MyError
        if not Misc.actives.sprites() and not self.letter_objects:
            self.stop = -1
        return self.stop,self.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 "Fallingletters"


    def helptitle(self):
        return _("Fallingletters")
    
    def help(self):
        text = [_("The aim of the game:"),
        _("Type the falling letters before they hit the ground."),
        " ",
        _("Difficulty : 4-7 years"),
        " ",
        _("Number of levels : 3")]
        return text
