#!/usr/bin/env python
# vim: set ts=8 sw=4 sts=4 et:
#
# ANWB_Rijopleiding_Linux.py: een pygtk wrapper om de bestanden van de
# ANWB Examentraining Rijbewijs B.
#
# Gemaakt op 6 maart, 2010 door Walter Doekes. Licentie: public domain.
# Ja, de code is niet de mooiste die je gezien hebt. Maar over een paar
# dagen plan ik de CD toch nooit meer nodig te hebben ;-)
#
# N.B.: om dit script te gebruiken heb je wel de data van de CD-ROM
# nodig. De vragen/plaatjes zijn tenslotte auteursrechtelijk beschermd
# en mag ik er niet bij leveren.
#
# Veel succes met leren!
#
# (versie: 0.1, laatste edit: 2010-03-06 23:13, initial release)
# (versie: 0.2, laatste edit: 2010-03-10 20:37, decoden van cp1252)
# 
import codecs, gobject, gtk, os, random, sys, traceback


class AnwbWindow(object):
    IMAGE_SIZE = (495, 374) 

    def __init__(self, data, questions):
        self._data = data
        self._questions = list(reversed(questions)) # so we can pop
        self._right, self._wrong = 0, 0

        self._showing_dialog = False
    
        self._create_window()
        #self.set_head('Vraag #NNN')
        #self.set_image('/home/walter/Desktop/ANWB2008/Fdata/Gdata/100.jpg')
        #self.set_question('_\n_\n_\n_\n_')
        self._window.connect('key-press-event', self.update_answer_keypress)
        self.show()

    def last_question(self):
        return not bool(self._questions)

    def next_question(self):
        question_num = self._questions.pop()
        question = self._data.get_question(question_num)
        self.set_head('Vraag #%03d' % question_num)
        self.set_image(question.get('image'))
        self.set_question(question.get('question'))
        self.set_answer(question.get('answer'))
        self.set_explanation(question.get('explanation'))
        self.reset_pulse()

    def pulse(self):
        if self._showing_dialog:
            return
        self._progress_value += 0.03
        if self._progress_value >= 1.0:
            self._progress.set_fraction(1.0)
            self.check_answer('')
        else:
            self._progress.set_fraction(self._progress_value)
            gobject.timeout_add(500, self.pulse)

    def reset_pulse(self):
        self._progress_value = 0
        self._progress.set_fraction(0)
        gobject.timeout_add(500, self.pulse)

    def show(self):
        self._window.show_all()

    def show_dialog(self, title, body):
        self._showing_dialog = True
        dialog = gtk.Dialog(
            title=title,
            parent=self._window,
            flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
        )
        label = gtk.Label()
        label.set_justify(gtk.JUSTIFY_CENTER)
        label.set_line_wrap(True)
        label.set_text(body)
        dialog.vbox.add(label)
        dialog.show_all()
        dialog.run()
        dialog.destroy()
        self._showing_dialog = False

    def set_head(self, head):
        self._head.set_text(head) # Vraag #NNN

    def set_image(self, filename):
        self._image.set_from_file(filename) # Fdata/Gdata/123.jpg

    def set_question(self, question):
        self._question.set_text(question)

    def set_answer(self, answer):
        self._answer.set_text('')
        self._answer_value = answer

    def set_explanation(self, explanation):
        self._explanation_value = explanation

    def check_answer(self, answer):
        try:
            if isinstance(self._answer_value, float):
                answer = float(answer)
        except ValueError:
            answer = 'False'
        if answer == self._answer_value:
            self._right += 1
            text = 'Antwoord %s is JUIST!\n\n%s' % (self._answer_value, self._explanation_value)
        else:
            self._wrong += 1
            text = 'FOUT! Het goede antwoord had moeten zijn %s.\n\n%s' % (self._answer_value, self._explanation_value)
       
        self.show_dialog('Antwoord', text) 
        if self.last_question():
            self._quit(self)
        else:
            self.next_question()

    def update_answer_keypress(self, window, event):
	assert event.type == gtk.gdk.KEY_PRESS
        text = self._answer.get_text()
	if event.keyval in (gtk.keysyms.Return, gtk.keysyms.KP_Enter):
            if text != '':
                self.check_answer(text)
        elif event.keyval == gtk.keysyms.Escape:
            self._answer.set_text('')
        elif event.keyval == gtk.keysyms.BackSpace:
            if text != '':
                text = text[0:-1]
                self._answer.set_text(text)
        elif 0 <= event.keyval < 128 or 65456 <= event.keyval <= 65465:
            if event.keyval < 128:
                char = chr(event.keyval).upper()
            else:
                char = chr(event.keyval - 65456 + 48)
            if char in 'ABCJN0123456789,.':
                if char == ',': char = '.'
                if char == '.' and (text == '' or '.' in text): return # break
                if char in 'ABCJN': text = ''
                self._answer.set_text(text + char)

    def _create_window(self):
        self._window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self._window.connect('destroy', self._quit)
        self._window.set_border_width(10)

        box1v = gtk.VBox(homogeneous=False, spacing=10)
        self._window.add(box1v)

        self._head = gtk.Label()
        box1v.add(self._head)
        
        self._image = gtk.Image()
        self._image.set_size_request(*self.IMAGE_SIZE)
        box1v.add(self._image)

        self._progress = gtk.ProgressBar()
        box1v.add(self._progress)

        box11h = gtk.HBox(homogeneous=False, spacing=10)
        box1v.add(box11h)

        self._question = gtk.TextBuffer()
        question_view = gtk.TextView(buffer=self._question)
        # request a large size to take the most room in the hbox
        question_view.set_size_request(self.IMAGE_SIZE[0] - 120, -1)
        question_view.set_cursor_visible(False)
        question_view.set_editable(False)
        question_view.set_wrap_mode(gtk.WRAP_WORD)
        box11h.add(question_view)

        box111v = gtk.VBox(homogeneous=False, spacing=10)
        box11h.add(box111v)

        answer_label = gtk.Label()
        answer_label.set_text('Vul antwoord in\nen druk op enter.')
        box111v.add(answer_label)

        self._answer = gtk.Label()
        box111v.add(self._answer)

    def _quit(self, window):
        print 'Right answers: %d' % self._right
        print 'Wrong answers: %d' % self._wrong
        gtk.main_quit()


class AnwbData(object):
    QUESTIONS_PER_FILE = 50
    QUESTION_TYPES = ('', 'JN', 'ABC', '#', 'AB')
    QUESTION_PATH = os.path.join('Fdata', 'Data')
    IMAGE_PATH = os.path.join('Fdata', 'Gdata')

    def __init__(self, path):
        print 'Attempting to parse ANWB files in %s...' % path
        self._path = path
        self._questions = {}

        translator = codecs.getreader('cp1252') # or latin1 perhaps
        path = os.path.join(self._path, self.QUESTION_PATH)
        files = [i for i in os.listdir(path) if i.startswith('DATA') and i.endswith('.CFG')]
        for file in files:
            offset = (int(''.join([i for i in file if i in '0123456789'])) - 1) * self.QUESTIONS_PER_FILE
            print 'Parsing %s (offset %d)...' % (file, offset)
            try:
                self.parse_questions(translator(open(os.path.join(path, file), 'r')), offset)
            except:
                print traceback.format_exc()

        print 'Found and parsed %d questions...' % len(self._questions)
        path = os.path.join(self._path, self.IMAGE_PATH)
        files = [i for i in os.listdir(path) if i.endswith('.jpg')]
        image_count = 0
        for num in self._questions:
            image_file = '%03d.jpg' % num
            if image_file in files:
                self._questions[num]['image'] = os.path.join(path, image_file)
                image_count += 1
        print 'Found %d corresponding images...' % image_count

    def parse_questions(self, file, offset):
        while True:
            line = file.next().strip()
            if line.endswith('---'):
                num, unknown, qtype, rest = line.split(' ', 3)
                num, unknown, qtype = int(num), int(unknown), self.QUESTION_TYPES[int(qtype)]
                question = file.next().strip()
                if qtype in ('AB', 'ABC'):
                    question += '\nA: ' + file.next().strip()
                    question += '\nB: ' + file.next().strip()
                    if qtype == 'ABC':
                        question += '\nC: ' + file.next().strip()
                elif qtype == '#':
                    question += '\n(Antwoord in %s)' % file.next().strip()
                answer = file.next().strip().split()[0]
                explanation = file.next().strip()
                while True:
                    more = file.next().strip()
                    if more == '':
                        break
                    explanation += more

                if qtype == 'AB':
                    assert answer in ('A', 'B')
                elif qtype == 'ABC':
                    assert answer in ('A', 'B', 'C')
                elif qtype == 'JN':
                    answer = answer[0:1]
                    assert answer in ('J', 'N')
                elif qtype == '#':
                    answer = float(answer.replace(',', '.'))

                self._questions[offset + num] = {
                    'question': question,
                    'answer': answer,
                    'explanation': explanation,
                }
            else:
                # line is probably "*EOF"
                return

    def get_question(self, number):
        return self._questions[number]

    def get_numbers(self):
        return self._questions.keys()


def main(path=None, first_question=None, last_question=None, randomize=False):
    try:
        assert os.listdir(path)
        first_question = int(first_question)
        last_question = int(last_question)
        assert first_question >= 0 and first_question <= last_question
        randomize = bool(randomize)
    except (AssertionError, OSError, TypeError, ValueError):
        print >>sys.stderr, 'Usage: %s [path] [firstnum] [lastnum] [randomize]' % sys.argv[0]
        print >>sys.stderr, 'E.g.:  %s /media/cdrom 1 50' % sys.argv[0]
        print >>sys.stderr, 'or:    %s /media/cdrom 1 500 true' % sys.argv[0]
        sys.exit(1)

    try:
        data = AnwbData(path)
        questions = range(first_question, last_question + 1)
        question_nums = data.get_numbers()
        for question_num in questions:
            if question_num not in question_nums:
                raise RuntimeError('Question number %d not found!' % question_num)
        if randomize:
            random.shuffle(questions)
    except Exception, e:
        print >>sys.stderr, 'Error: %s' % e
        sys.exit(1)

    # Create the UI
    window = AnwbWindow(data, questions)
    window.next_question()
    # Enter the main event loop
    gtk.main()


if __name__ == '__main__':
    main(*sys.argv[1:])
