#!/usr/bin/env python
# Hopefully this is free software, you should be able to use, modify,
# and redistribute non-commercially freely, as long as you grant
# these same rights to anyone who wishes to have them... or so... :-)

# An interactive lerning tool of the flip-card kind.
# see http://www.lojban.org/tiki/tiki-index.php?page=Parallel+2 for
# further information

# 30/01/07, thkruege at googlemail dot com
# made radical changes and therefore introduced next version (0.3)
# Is it ok to regard this as GPLed software?
# (for changes, see below)

# 20/01/06 curv (Kio) Modified to fit on smaller screens,
# replaced string.rjust() with string.zfill(), changed default
# colours to be easier on the eyes. (Matter of opinion of course)

# December 2005, Yann Le Du http://yledu.free.fr
# Second improved version

# Directions for use :
# Prerequisites : python, pygame with sdl and ttf, your favourite
# wav-player (default: mplayer), data.tar.gz (Parallel 2 data files from
# http://www.lojban.org/tiki/tiki-download_wiki_attachment.php?attId=225&download=y )
# run this program from the directory where the parallel 2 data files
# reside. Or specify a path to the data directory below
# Run it with python by typing "python parallel03.py"

# Changes by thkruege at googlemail dot com:

# - introduced proper versioning
# - keyboard commands now pygame based, therefore:
# - does not need a terminal for inputs any more
# - does not depend on sdl-mixer any more (for now)
# - plays audio files using custom command (or not at all)
#     (this got rid of the rather nasty realplayer dependency)
# - data can now reside in arbitrary locations
# - slightly changed "gameplay"
#     (now you only listen to sounds by pressing an extra key)
# - threw away some redundant code
# - threw away unused variables
# - reorganized code so that global variables are easyier to access
#     for non developers
# - fixed SDL-ttf-render fail with some voodoo
# - fixed a typo ;-)


# TODO:
# - allow keyboard interrupts in scrolling/random mode
# - get rid of all those ugly hacks
# - get rid of all this hardcoded stuff
# - implement audio output via pygame (sdl-mixer)
# - clean up code, do object orientation
# - implement more colours
# - document properly
# - ask author if its ok to license under GNU GPL
# - (maybe repackage data.tar.gz with compressed audio files)

# CREDITS:
# Original idea and Windows version "Parallel 2" by Yanis Batura
# Some pygame technique taken from "Learnfast" program by Michal Wallace

### globals ########################################################

# if the data does not reside in the path parallel.py is called from,
# adjust the datapath below
DATAPATH = "./"

# Set the player command here, possible alternatives are (depending on your
# system): wavplay, realplay, play, mplayer
# (HINT: to disable sound, do NOT comment out the line below, simply
# enter "true" as value, but _keep the quotation marks_)
# The command is executed with an additional space and the path to the
# *.wav file appended.
# Also note, that most players possibly block the sound device while
# playing, so if data is scrolled too fast, audio files could be
# dropped.
# (std.out is redirected to /dev/null)
PLAYERCOMMAND = "mplayer"

# Set your prefered window geometry here.
# below (790, 400) things get ugly, though :-)
WINSIZE = (790, 400)

YELLOW = 255, 255,  0
BLACK = 0,     0,   0
CYAN = 0,   255,   255

TOPLINE, TOPLOWLINE, BOTTOMHIGHLINE,BOTTOMLINE = -2,-1,1,2

CENTER = (WINSIZE[0]/2,WINSIZE[1]/2)

FOOTER = WINSIZE[1]


### imports ########################################################

import os, sys, string, time
import random, math, pygame
from pygame.locals import *
import pygame.font
import pygame.key
import random


### defs ###########################################################


#TODO: bring into alphabetical order
def getkeys():
    for event in pygame.event.get():
        # change on key release feels nicer
        if event.type == KEYUP: 
            if event.key == K_ESCAPE:
                sys.exit()
                # return "esc" (possible alternative)
            elif event.key == K_f:
                return "f"
            elif event.key == K_b:
                return "b"
            elif event.key == K_p:
                return "p"
            elif event.key == K_g:
                return "g"
            elif event.key == K_m:
                return "m"
            elif event.key == K_q:
                return "q"
            elif event.key == K_n:
                return "n"
            elif event.key == K_s:
                return "s"
            elif event.key == K_h:
                return "h"
            elif event.key == K_0:
                return "0"
            elif event.key == K_1:
                return "1"
            elif event.key == K_2:
                return "2"
            elif event.key == K_3:
                return "3"
            elif event.key == K_4:
                return "4"            
            else:
                print "undefined key"
                return "undefined"
        else:
            return None



def drawtext(screen, font, text, color, line):
    w,h = font.size(text)
    if line==TOPLINE:
        y = CENTER[1]-font.get_height()*2
    elif line==TOPLOWLINE:
        y = CENTER[1]-font.get_height()/2*1.7
    elif line==BOTTOMHIGHLINE:
        y = CENTER[1]+font.get_height()/2*1.7
    elif line==BOTTOMLINE:
        y = CENTER[1]+font.get_height()*2
    else:
        raise Exception("No such position variable: ", line)
    
    # hack: changed x2 to 1 from 0, fixed (my) SDL-ttf render fail
    # if you get an error at this line, try changing back to:
    # screen.blit(font.render(text, 0, color), (CENTER[0]-(w/2), y))
    screen.blit(font.render(text, 1, color), (CENTER[0]-(w/2), y))


def init():
    pygame.init()
    screen = pygame.display.set_mode(WINSIZE)
    pygame.display.set_caption("Parallel")
    screen.fill(BLACK)
    return screen

# ugly hack, remove

screen = init()

def printatheight(text,height):
    font = pygame.font.Font(None, 23)
    w,h=font.size(text)
    screen.blit(font.render(text, 1, YELLOW), (CENTER[0]-(w/2),height-h))
    pygame.display.flip()

def store():
    """
    Stores the English/Lojban examples for easy retrieval
    """
    level=None

    printatheight("Which level do you want? 0 (pronounciation), 1 (easy), 2, 3, 4 (hard)?",CENTER[1])
    printatheight("press either 0 1 2 3 4 or esc (to quit)", FOOTER)
    
    while level not in ["0","1","2","3","4"]:
        level = getkeys()

    if level == "0":
        datafile = "Lojban Pronunciation.txt"
        datadir = "Pronunciation"
    else:
        datafile="Lojban" + level.zfill(3) + "_Eng.txt"
        datadir = "Lojban_" + level.zfill(3)
    
        
    f=open(DATAPATH+"/"+datafile,"r")
    total=f.readlines()
    f.close()

    counter=0
    symplus=False
    toplay=""
    working = total[1:]+["  "]
    timefortwo = .4
    if level == "0":
        timeforspace = 1.1
    else:
        timeforspace = .3

    storage=[] # Contains the English sentence and Lojban translation, it contains a series of "toadd", i.e. a series of examples

    # One "toadd" for each example Structure is :
    # [[1st English, 2nd English, etc.],[1st Lojban, 2nd Lojban, etc.],[wavfilename,time of play]]. An example may have multiple sentences, i.e. question+answer
    toadd=[[],[],[]]
    
    for el in working:
        if el[1]=="+":
            word = string.join(el[2:].split("\x00"),"").strip()
            if symplus: # In this case a Lojban answer to a question
                symplus = False
                toadd[1].append(word)
                much = sum([len(m)/2.*timefortwo for m in word.split(" ")])+word.count(" ")*timeforspace # Time for pronunciation
                toadd[2][1] += much # Add time for answer
            else: # In that case an English answer to a question
                symplus = True
                toadd[0].append(word)

        elif el[1]=="`": # In this case, we have the Lojban sentence
            counter=string.join(el.split("`")[1].split("\x00"),"").strip() # Voodoo for extracting Wav file number from unicode string
            toplay = datadir+os.sep+counter+".wav" # Wav file name

            word = string.join(el.split("`")[2].split("\x00"),"").strip() # Voodoo for extracting sentence from unicode string
            much = sum([len(m)/2.*timefortwo for m in word.split(" ")])+word.count(" ")*timeforspace # Time for pronunciation

            toadd[1].append(word)
            toadd[2].append(toplay)
            toadd[2].append(much)
            
        else: # In that case, we have the English sentence
            storage.append(toadd) # We have a new English example, so we store the previous one...
            toadd=[[],[],[]] # and a new example begins
            word = string.join(el.split("\x00"),"").strip()
            toadd[0].append(word)

    return storage[1:],level



def main():


    font = pygame.font.Font(None, 30)


    storage,level=store()

    automatise=""
    screen.fill(BLACK)
    printatheight("MANUAL (recommended) (press key for next/previous example and translation)",CENTER[1]-30)
    printatheight("GUESS (examples given RANDOMLY one after the other with short",CENTER[1]-10)
    printatheight("delay between Enlgish and Lojban displays to find translation)",CENTER[1]+10)
    printatheight("SCROLLING (examples given RANDOMLY one after the other with English/Lojban displayed simultaneously)",CENTER[1]+30)
    printatheight("NOTE: GUESS and SCROLLING can not yet be conveniently interrupted, use with caution!",CENTER[1]-70)
    printatheight("press either m(anual) g(uess) s(croll) or esc (to quit)",FOOTER)
    
    while automatise not in ["m","g","s"]:        
        automatise = getkeys()
    screen.fill(BLACK)
    



    tick, tock = YELLOW, BLACK
    this, next = tick, tock

    numexamples = len(storage)
    poslist = range(numexamples) # List of examples' positions
    

    pos = 0
    choice=None
    numex=0
    shown = False

    while True:

        if automatise != "m":

            if poslist == []:
                break
            numex += 1
            pos = random.randrange(len(poslist)) # Random example for Learning and Scrolling mode
            el = storage[poslist[pos]]
            poslist.pop(pos) # Remove the index of the example drawn

        else:
            numex = pos+1 # If automatise != "M" pos and poslist[pos] are equal
            el = storage[pos]
            
        


        pygame.display.set_caption("Parallel - Level " + level + " - Example number " + str(numex) + "/" + str(numexamples))
        drawtext(screen, font, el[0][0], YELLOW, TOPLINE)
        pygame.display.flip()
        if len(el[0])==2:
            drawtext(screen, font, el[0][1], YELLOW, BOTTOMHIGHLINE)
            pygame.display.flip()
            
        if automatise == "m":
            
            
            p = "(p)revious" * (pos > 0)
            n = "(n)ext" * (pos < numexamples-1)
            choice = None
            printatheight("press either"+p+" "+n+" h(ear) or esc (to quit)",FOOTER)
            while choice not in ["n"*(pos < numexamples-1), "p"*(pos > 0), "h"]:                
                choice = getkeys();
            screen.fill(BLACK)    

                          
            if choice == "h":
                os.system(PLAYERCOMMAND+" "+el[2][0]+"> /dev/null &") # Play the wav
            elif choice == "n": #and n != "":
                if shown == True:
                    pos+=1
                    shown = False
                    continue
                else:
                    shown = True
                    pass
            elif choice == "p": #and p != "":
                pos-=1
                continue
            else:
                pass
            
        elif automatise == "g":
            if level != "0":
                time.sleep(el[2][1])
            else:
                pass
        
        drawtext(screen, font, el[1][0], CYAN, TOPLOWLINE)
        pygame.display.flip()

        if automatise != "m":
            time.sleep(el[2][1])
            screen.fill(BLACK)

        
        if len(el[1])==2:
            drawtext(screen, font, el[1][1], CYAN, BOTTOMLINE)
            pygame.display.flip()



if __name__=="__main__":
    main()
