# GNU Solfege - free ear training software
# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008  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/>.

import logging
import gtk, gobject
import abstract, statistics, statisticsviewer, gu, random
import lessonfile
import utils

class Teacher(abstract.Teacher):
    def __init__(self, exname, app):
        abstract.Teacher.__init__(self, exname, app)
        self.lessonfileclass = lessonfile.HeaderLessonfile
        # 60, 84, 100, 120
        self.m_ped = [60, 120, 40, 168, 208, 80, 96, 139, 48, 69, 108, 192, 152,
                      88, 63, 100, 116, 176, 200, 52, 76, 126, 160,
                      84, 56, 44, 66, 72, 92, 104, 112, 132, 144, 184]
        self.m_bpms = [40, 44, 48, 52, 56, 60, 63, 66, 69,      # 0 - 8
                       72, 76, 80, 84, 88, 92, 96, 100, 104,    # 9 - 17
                       108, 112, 116, 120, 126, 132, 138, 144,  # 18 - 25
                       152, 160, 168, 176, 184, 192, 200, 208]  # 26 - 33
        self.m_statistics = statistics.LessonStatistics(self)
        self.m_timeout_tag = None
        self.m_question = None
        self.m_practise_these = {}
        v = self.get_list('active_bpms')
        for bpm in self.m_bpms:
            if bpm in v:
                self.m_practise_these[bpm] = 1
            else:
                self.m_practise_these[bpm] = 0
    def toggle_active(self, bpm):
        self.m_practise_these[bpm] = not self.m_practise_these[bpm]
        v = [bpm for bpm in self.m_practise_these if self.m_practise_these[bpm]]
        self.set_string('active_bpms', str(v))
    def get_possible_bpms(self):
        return [n for n in self.m_practise_these.keys() \
            if self.m_practise_these[n]]
    def get_number_of_levels(self):
        return len(self.m_ped)
    def new_question(self):
        last = self.m_question
        if not self.get_possible_bpms():
            return False

        self.m_question = random.choice(self.get_possible_bpms())
        while self.m_question == last and len(self.get_possible_bpms()) > 1:
            self.m_question = random.choice(self.get_possible_bpms())
        self.start_question(self.m_question)
        self.q_status = self.QSTATUS_NEW
        return True
    def start_question(self, bpm):
        self.tick()
        if self.m_timeout_tag:
            gobject.source_remove(self.m_timeout_tag)
        self.m_timeout_tag = gobject.timeout_add(60000/bpm, self.tick)
    def stop_question(self):
        if self.m_timeout_tag:
            gobject.source_remove(self.m_timeout_tag)
            self.m_timeout_tag = None
    def tick(self, _o=None):
        utils.play_perc(8, 60)
        return 1
    def guess_answer(self, bpm):
        assert self.q_status != self.QSTATUS_NO
        if self.m_question == bpm:
            self.stop_question()
            if self.q_status == self.QSTATUS_NEW:
                self.m_statistics.add_correct(unicode(bpm))
            self.q_status = self.QSTATUS_SOLVED
            self.m_app.play_happy_sound()
            return 1
        else:
            if self.q_status == self.QSTATUS_NEW:
                self.m_statistics.add_wrong(str(self.m_question), unicode(bpm))
            self.q_status = self.QSTATUS_WRONG
            self.m_app.play_sad_sound()
    def end_practise(self):
        super(Teacher, self).end_practise()
        self.stop_question()
        self.q_status = self.QSTATUS_NO
    def give_up(self):
        self.stop_question()
        logging.debug("identifybpm.give_up:FIXME not saving statistics")
        self.q_status = self.QSTATUS_GIVE_UP

class Gui(abstract.Gui):
    def __init__(self, teacher, window):
        abstract.Gui.__init__(self, teacher, window)
        ################
        # practise_box #
        ################
        self.practise_box.set_spacing(gu.PAD)
        self.m_buttons = []
        vbox = gu.bVBox(self.practise_box, False)
        for s, e in ((0, 9), (9, 18), (18, 26), (26, 34)):
            box = gtk.HBox()
            vbox.pack_start(box)
            for i in range(s, e):
                bpm = self.m_t.m_bpms[i]
                button = gtk.Button(str(bpm))
                box.pack_start(button)
                button.connect('clicked', self.on_click)
                button.connect('event', self.on_event)
                button.set_data('bpm', bpm)
                self.m_buttons.append(button)

        self.g_flashbar = gu.FlashBar()
        self.g_flashbar.show()
        self.practise_box.pack_start(self.g_flashbar, False)

        self.g_new = gu.bButton(self.action_area, _("_New tempo"), self.on_new)
        self.g_give_up = gu.bButton(self.action_area, _("_Give up"), self.on_give_up)
        self.g_give_up.set_sensitive(False)
        self.practise_box.show_all()
        ##############
        # statistics #
        ##############
        self.g_statview = statisticsviewer.StatisticsViewer(self.m_t.m_statistics, _('Bpm'))
        self.g_statview.show()
        self.g_notebook.append_page(self.g_statview,
                                    gtk.Label(_("Statistics")))
        ########
        # init #
        ########
        self.g_notebook.get_nth_page(1).hide()
        self.update_buttons()
    def on_event(self, button, event):
        if event.type == gtk.gdk.BUTTON_RELEASE and event.button == 3:
            self.m_t.toggle_active(button.get_data('bpm'))
            self.update_buttons()
    def on_level_change(self, adjustment):
        self.set_int('level', adjustment.value)
        self.update_buttons()
    def on_new(self, _o=None):
        try:
            n = self.m_t.new_question()
        except Exception, e:
            if not self.standard_exception_handler(e):
                raise
            self.m_t.stop_question()
            return
        if n:
            self.g_new.set_sensitive(
              not self.get_bool('config/picky_on_new_question'))
            self.g_give_up.set_sensitive(True)
            self.m_buttons[0].grab_focus()
        else:
            self.g_flashbar.flash(_("You have to select some tempos to practise."))
    def on_give_up(self, _o):
        self.m_t.give_up()
        self.g_flashbar.flash(str(self.m_t.m_question))
        self.g_new.set_sensitive(True)
        self.g_give_up.set_sensitive(False)
    def update_buttons(self):
        v = self.m_t.get_possible_bpms()
        for b in self.m_buttons:
            if b.get_data('bpm') in v:
                b.get_children()[0].set_name("BpmActiveLabel")
            else:
                b.get_children()[0].set_name("BpmInactiveLabel")
    def on_click(self, _o):
        if self.m_t.q_status in (self.QSTATUS_SOLVED, self.QSTATUS_GIVE_UP):
            return
        if _o.get_data('bpm') not in self.m_t.get_possible_bpms():
            return
        if self.m_t.q_status == self.QSTATUS_NO:
            self.g_flashbar.flash(_("Click 'New tempo' to begin."))
            return
        if self.m_t.guess_answer(_o.get_data('bpm')):
            self.g_flashbar.flash(_("Correct, it is %i") % _o.get_data('bpm'))
            self.g_give_up.set_sensitive(False)
            self.g_new.set_sensitive(True)
            self.g_new.grab_focus()
        else:
            self.g_flashbar.flash(_("Wrong"))
        self.g_statview.update()
    def on_start_practise(self):
        self.g_flashbar.flash(_("Click 'New tempo' to begin."))
        self.g_new.grab_focus()
        self.g_statview.update()
        self.m_t.m_statistics.reset_session()
    def on_end_practise(self):
        self.m_t.end_practise()
        self.g_new.set_sensitive(True)
        self.g_give_up.set_sensitive(False)
