# GNU Solfege - ear training for GNOME
# Copyright (C) 2000, 2001, 2002, 2003, 2004  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 2 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

__exercise_data__ = {
    'exercise_name': 'melodic-interval',
    'menu_path': '%s/%s' % (_("_Intervals"), _("_Melodic intervals")),
    'stock-icon': 'solfege-melodic-interval',
    'toolbar': 3,
}
import exercise_setup
exercise_setup.register_exercise(__exercise_data__)

import gtk, gnome, gobject
import gu, widgets, inputwidgets
import abstract, const
import statistics, statisticsviewer
import soundcard, utils, mpd

class Teacher(abstract.MelodicIntervalTeacher):
    exercise_data = __exercise_data__
    def __init__(self, exname, app, config):
        abstract.MelodicIntervalTeacher.__init__(self, exname, app, config)
        self.m_statistics = statistics.IntervalStatistics(self)
        self.m_timeout_handle = None
    def give_up(self):
        """This function is only called *after* the user already has
        answered wrong once, so the statistics are already updated.
        """
        self.q_status = const.QSTATUS_GIVE_UP
    def guess_answer(self, answer, directions=None):
        """
        return TRUE value if correct
        """
        assert self.q_status not in (const.QSTATUS_NO, const.QSTATUS_GIVE_UP)
        if directions:
            q = self.m_question
        else:
            q = map(abs, self.m_question)
        if q == answer:
            if self.q_status == const.QSTATUS_NEW:
                if self.get_int('number_of_intervals=1') == 1:
                    self.m_statistics.add_correct(self.m_question[0])
            self.maybe_auto_new_question()
            self.q_status = const.QSTATUS_SOLVED
            return 1
        else:
            if self.q_status == const.QSTATUS_NEW:
                if self.get_int('number_of_intervals=1') == 1:
                    self.m_statistics.add_wrong(self.m_question[0], answer[0])
            self.q_status = const.QSTATUS_WRONG


class Gui(abstract.IntervalGui):
    def __init__(self, teacher, window):
        abstract.IntervalGui.__init__(self, teacher, window)
        self.g_give_up = gu.bButton(self.action_area, _("_Give up"), self.give_up)
        self.g_give_up.set_sensitive(False)
        ##############
        # config_box #
        ##############
        self.g_mici = widgets.MultipleIntervalConfigWidget(self.m_exname)
        self.config_box.pack_start(self.g_mici, False, False)
        self.config_box.pack_start(gtk.HBox(), False,
                                   padding=gnome.ui.PAD_SMALL)
        hbox = gu.bHBox(self.config_box, False)
        hbox.set_spacing(gnome.ui.PAD)
        hbox.pack_start(gtk.Label(_("Try to keep question with range from")),
                        False)
        lowspin = widgets.NotenameSpinButton(self.get_string('lowest_tone'))
        hbox.pack_start(lowspin, False)
        gu.bLabel(hbox, _("to"), False)
        highspin = widgets.NotenameSpinButton(self.get_string('highest_tone'))
        hbox.pack_start(highspin, False)
        self.m_notenamerange = widgets.nNotenameRangeController(lowspin, highspin,
                           mpd.LOWEST_NOTENAME, mpd.HIGHEST_NOTENAME,
                           self.m_t.m_exname, 'lowest_tone', 'highest_tone')

        self._add_auto_new_question_gui(self.config_box)
        # ----------------------------------
        hbox = gu.bHBox(self.config_box, False)
        hbox.set_spacing(gnome.ui.PAD_SMALL)
        gu.bLabel(hbox, _("Input interface:"), False)

        optionmenu = gtk.OptionMenu()
        menu = gtk.Menu()
        for i in range(len(inputwidgets.inputwidget_names)):
            item = gtk.MenuItem(inputwidgets.inputwidget_names[i])
            menu.add(item)
            item.connect('activate',
                         lambda widget, i=i: self.use_inputwidget(i))
        optionmenu.set_menu(menu)
        optionmenu.set_history(self.get_int('inputwidget'))
        hbox.pack_start(optionmenu, False)

        self.g_disable_unused_buttons = gu.nCheckButton(self.m_exname,
                    'disable_unused_intervals', _("_Disable unused buttons"))
        hbox.pack_start(self.g_disable_unused_buttons)
        self.config_box.show_all()
        ##############
        # statistics #
        ##############
        self.setup_statisticsviewer(statisticsviewer.StatisticsViewer,
                                    _("Melodic interval"))
        self.select_inputwidget()
    def give_up(self, _o=None):
        if self.m_t.q_status == const.QSTATUS_WRONG:
            s = utils.int_to_intervalname(self.m_t.m_question[0],
                       len(self.m_t.m_question) > 1, 1)
            for n in self.m_t.m_question[1:]:
                s = s + "+" + utils.int_to_intervalname(n,
                       len(self.m_t.m_question) > 1, 1)
            self.g_flashbar.push(_("The answer is: %s") % s)
            self.m_t.give_up()
            self.g_new_interval.set_sensitive(True)
            self.g_new_interval.grab_focus()
            self.g_give_up.set_sensitive(False)
    def get_interval_input_list(self):
        v = []
        for x in range(self.get_int('number_of_intervals')):
            for i in self.get_list('ask_for_intervals_%i' % x):
                if not (abs(i) in v):
                    v.append(abs(i))
        v.sort()
        return v
    def click_on_interval(self, mouse_button, interval, midi_int):
        """The key bindings are also directed here.
        """
        if mouse_button != 1:
            return
        if self.m_t.q_status == const.QSTATUS_NO:
            self.g_flashbar.flash(_("Click 'New interval' to begin."))
            return
        if midi_int:
            # midi_int is only set when we use some of the instrument widgets,
            # not when we use the buttons interface.
            soundcard.play_note(self.get_int('config/preferred_instrument'),
                        4, 0, midi_int,
                        self.get_int('config/preferred_instrument_velocity'))
        if self.m_t.q_status == const.QSTATUS_GIVE_UP:
            return
        if self.m_t.q_status == const.QSTATUS_SOLVED:
            self.g_flashbar.flash(_("You have already identified this interval"))
            return
        if not (-17 < interval < 17):
            self.g_flashbar.flash(_("Ignoring intervals greater than decim."))
            self.g_input.forget_last_tone()
            return
        self.m_answer.append(interval)
        if not self.msg:
            self.msg = utils.int_to_intervalname(interval, 1, 1)
        else:
            self.msg = self.msg + "+" + utils.int_to_intervalname(interval, 1, 1)
        self.g_flashbar.flash(self.msg)
        if len(self.m_answer) == self.m_number_of_intervals_in_question:
            if self.m_t.guess_answer(self.m_answer,
                                     self.g_input.know_directions()):
                self.g_flashbar.flash(_("Correct"))
                self.g_new_interval.set_sensitive(True)
                self.g_new_interval.grab_focus()
                self.g_give_up.set_sensitive(False)
            else:
                self.g_flashbar.flash(_("Wrong"))
                if self.get_bool("config/auto_repeat_question_if_wrong_answer"):
                    self.m_t.play_question()
                self.g_give_up.set_sensitive(True)
            self.m_answer = []
            self.g_input.set_first_note(self.m_t.m_tonika)
            self.msg = ""
    def new_question(self, _o=None):
        self.msg = ""
        self.m_number_of_intervals_in_question \
               = self.get_int('number_of_intervals')
        self.m_answer = []
        ret = self.m_t.new_question(self.get_string('lowest_tone'),
                                    self.get_string('highest_tone'))
        if ret == Teacher.ERR_CONFIGURE:
            self.g_flashbar.clear()
            self.g_flashbar.flash(_("The exercise has to be better configured."))
            self.g_repeat.set_sensitive(False)
        elif ret == Teacher.OK:
            self.g_repeat.set_sensitive(True)
            self.g_give_up.set_sensitive(False)
            self.g_input.set_first_note(self.m_t.m_tonika)
            self.m_t.play_question()
            self.g_new_interval.set_sensitive(
                  not self.get_bool('config/picky_on_new_question'))
            self.g_flashbar.clear()
            #inputwidget 0 is always the buttons.
            if self.get_int('inputwidget') == 0:
                self.g_input.grab_focus_first_sensitive_button()
        elif ret == Teacher.ERR_PICKY:
            self.g_flashbar.flash(_("You have to solve this question first."))
    def on_start_practise(self):
        self.m_t.m_statistics.reset_session()
        self.g_new_interval.grab_focus()
        gobject.timeout_add(const.SHORT_WAIT, lambda self=self:
            self.g_flashbar.flash(_("Click 'New interval' to begin.")))
    def on_end_practise(self):
        self.m_t.end_practise()
        self.g_new_interval.set_sensitive(True)
        self.g_repeat.set_sensitive(False)
        self.g_give_up.set_sensitive(False)
        self.g_input.clear()
        self.g_flashbar.clear()
