# GNU Solfege - free ear training software
# Copyright (C) 2010  Tom Cato Amundsen
#
# 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 3 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import

import logging

import gtk

from solfege import abstract
from solfege.mpd import elems
from solfege import gu
from solfege import lessonfile
from solfege import mpd
from solfege import soundcard

from solfege.mpd.parser import Lexer
from solfege.mpd.duration import Duration
from solfege.mpd.rat import Rat
from solfege.rhythmwidget import RhythmWidget, Controller

class Teacher(abstract.Teacher):
    def __init__(self, exname):
        abstract.Teacher.__init__(self, exname)
        self.lessonfileclass = lessonfile.QuestionsLessonfile
    def new_question(self):
        if self.get_bool('config/picky_on_new_question') \
                and self.q_status in [self.QSTATUS_NEW, self.QSTATUS_WRONG]:
            return self.ERR_PICKY
        self.q_status = self.QSTATUS_NEW
        self.m_P.select_random_question()
    def play_question(self):
        q = self.m_P.play_question()
    def get_empty_staff(self):
        """
        Return a score with and empty staff with the correct numbers of bars
        required to answer the current question.
        """
        score = elems.Score()
        score.add_staff(staff_class=elems.RhythmStaff)
        if 'rhythm' in self.m_P.get_question():
            music = self.m_P.get_question()['rhythm'].get_mpd_music_string(self.m_P)
        else:
            music = self.m_P.get_question()['music'].get_mpd_music_string(self.m_P)
        for bar in mpd.parser.parse_to_score_object(music).m_bars:
            score.add_bar(bar.m_timesig)
        score.voice11.fill_with_skips()
        return score
    def guess_answer(self, score):
        assert self.q_status not in (self.QSTATUS_NO, self.QSTATUS_GIVE_UP)
        q = self.m_P.get_question()
        if 'rhythm' in q:
            qvar = 'rhythm'
        else:
            qvar = 'music'
        qscore = mpd.parser.parse_to_score_object(
            q[qvar].get_mpd_music_string(self.m_P))
        if qscore.get_timelist() == score.get_timelist():
            self.q_status = self.QSTATUS_SOLVED
            return True
        else:
            self.q_status = self.QSTATUS_WRONG
            return False
    def give_up(self):
        self.q_status = self.QSTATUS_GIVE_UP


class Gui(abstract.LessonbasedGui):
    def __init__(self, teacher):
        abstract.LessonbasedGui.__init__(self, teacher)
        self.g_w = RhythmWidget()
        self.practise_box.pack_start(self.g_w, False)
        self.g_c = Controller(self.g_w)
        self.practise_box.pack_start(self.g_c, False)
        self.g_flashbar = gu.FlashBar()
        self.practise_box.pack_start(self.g_flashbar, False)
        self.g_flashbar.show()
        self.std_buttons_add(
            ('new', self.new_question),
            ('guess_answer', self.guess_answer),
            ('repeat', self.repeat_question),
            ('give_up', self.give_up))
        self.g_w.show()
    def new_question(self, *w):
        def exception_cleanup():
            self.m_t.q_status = self.QSTATUS_NO
            soundcard.synth.stop()
            self.std_buttons_exception_cleanup()
        self.m_t.new_question()
        try:
            self.m_t.play_question()
            self.std_buttons_new_question()
            self.g_w.grab_focus()
            self.g_w.set_score(self.m_t.get_empty_staff())
            self.g_c.set_editable(True)
        except Exception, e:
            if isinstance(e, mpd.MpdException):
                if 'm_mpd_badcode' not in dir(e):
                    e.m_mpd_badcode = self.m_t.m_P.get_question()['music'].get_err_context(e, self.m_t.m_P)
            if not self.standard_exception_handler(e, exception_cleanup):
                raise
    def guess_answer(self, *w):
        if self.m_t.q_status == Teacher.QSTATUS_SOLVED:
            if self.m_t.guess_answer(self.g_w.m_score):
                self.g_flashbar.flash(_("Correct, but you have already solved this question"))
            else:
                self.g_flashbar.flash(_("Wrong, but you have already solved this question"))
        else:
            if self.m_t.guess_answer(self.g_w.m_score):
                self.g_flashbar.flash(_("Correct"))
                self.std_buttons_answer_correct()
            else:
                self.g_flashbar.flash(_("Wrong"))
                self.std_buttons_answer_wrong()
                self.g_w.grab_focus()
    def repeat_question(self, *w):
        self.g_w.grab_focus()
        self.m_t.play_question()
    def give_up(self, *w):
        if 'rhythm' in self.m_t.m_P.get_question():
            varname = 'rhythm'
        else:
            varname = 'music'
        answer = mpd.parser.parse_to_score_object(
                self.m_t.m_P.get_question()[varname].get_mpd_music_string(
                    self.m_t.m_P))
        answer.m_staffs.append(self.g_w.m_score.m_staffs[0])
        answer.m_staffs[-1].set_parent(answer)
        answer.m_staffs[0].m_label = _("The music played:")
        answer.m_staffs[-1].m_label = _("The rhythm you entered:")
        answer.create_shortcuts()
        self.g_w.set_score(answer, cursor=None)
        self.m_t.give_up()
        self.g_c.set_editable(False)
        self.std_buttons_give_up()
    def on_start_practise(self):
        super(Gui, self).on_start_practise()
        self.std_buttons_start_practise()
        self.g_c.set_editable(False)
        self.g_w.set_score(elems.Score())
        self.g_flashbar.delayed_flash(self.short_delay,
            _("Click 'New' to begin."))

