# 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

# IMPORTANT:
# Remember to increase statistics_version and add code to handle
# old versions if incompatabilities are added.

import sys
if __name__ == '__main__':
    sys.path.append("src")
    sys.path.append(".")
    import i18n
    i18n.setup_srcdir()

import time, os, pickle
import shutil
import utils
import configureoutput

# version 1, the first version was introduced with Solfege 2.0
statistics_version = 1

STATISTICS_SAVE_DIR = os.path.join(os.path.expanduser('~'), '.solfege')
if not os.path.exists(STATISTICS_SAVE_DIR):
    for ver in '1.9', '1.4':
        if os.path.exists(os.path.join(os.path.expanduser('~'), '.solfege%s' % ver)):
            shutil.copytree(os.path.join(os.path.expanduser('~'), '.solfege%s' % ver),
                            STATISTICS_SAVE_DIR)
            break
    if not os.path.exists(STATISTICS_SAVE_DIR):
        os.mkdir(STATISTICS_SAVE_DIR)
    f = file(os.path.join(STATISTICS_SAVE_DIR, 'statistics_version'), 'w')
    print >> f, statistics_version
    f.close()

def get_statistics_version():
    """
    Return as an int, the version number the statistics stored in
    STATISTICS_SAVE_DIR has.
    """
    try:
        f = file(os.path.join(STATISTICS_SAVE_DIR, 'statistics_version'), 'r')
    except IOError, e:
        print >> sys.stderr, "While opening the file $HOME/.solfege/statistics_version:"
        print >> sys.stderr, e
        return 0
    s = f.read()
    f.close()
    try:
        return int(s)
    except ValueError, e:
        print >> sys.stderr, "While parsing the content of $HOME/.solfege/statistics_version:"
        print >> sys.stderr, e
        return 0

statistics_on_disk_version = get_statistics_version()
if statistics_on_disk_version > statistics_version:
    usable_statistics = None
    print >> sys.stderr, """The statistics stored in $HOME/.solfege have been created by a newer version of
Solfege, and that release has changed the way statistics are saved. If you want
to record statistics, you must either use that newer version of Solfege, or
delete (or rename) the directory $HOME/.solfege\n"""
elif statistics_on_disk_version == 0:
    usable_statistics = None
    print >> sys.stderr, """
Something is strange with the statistics stored in $HOME/.solfege.
"""
else:
    usable_statistics = 1
YEAR = 0
MONTH = 1
DAY = 2
HOURS = 3
MINUTES = 4
SECONDS = 5
WEEKDAY = 6
JULIANDAY = 7

class AbstractStatistics:
    def __init__(self, teacher):
        self.m_t = teacher
        self.m_session_stat = {}
        self.m_today_stat = {}
        self.m_last7_stat = {}
        self.m_total_stat = {}
        self.m_dicts = {'session' : self.m_session_stat,
                        'today'   : self.m_today_stat,
                        'last7'   : self.m_last7_stat,
                        'total'   : self.m_total_stat}
        self.m_savepath = os.path.join(STATISTICS_SAVE_DIR, self.m_t.m_exname)
        if usable_statistics:
            if not os.path.exists(self.m_savepath):
                os.mkdir(self.m_savepath)
    def _add(self, question, answer):
        for D in [self.m_session_stat, self.m_today_stat,
                  self.m_last7_stat, self.m_total_stat]:
            if not D.has_key(question):
                D[question] = {}
            if not D[question].has_key(answer):
                D[question][answer] = 0
            D[question][answer] = D[question][answer] + 1
    def add_correct(self, answer):
        self._add(answer, answer)
    def add_wrong(self, question, answer):
        self._add(question, answer)
    def merge_stat_dicts(self, A, B):
        "Add content of B to A"
        for k in B.keys():
            if not A.has_key(k):
                A[k] = {}
            for n in B[k].keys():
                if not A[k].has_key(n):
                    A[k][n] = 0
                A[k][n] = A[k][n] + B[k][n]
        return A
    def load_statistics(self, datadir):
        if not usable_statistics:
            return
        for filename in os.listdir(datadir):
            t = int(filename)
            lt = time.localtime(t)
            f = open(os.path.join(datadir, filename), 'r')
            D = pickle.load(f)
            f.close()
            now = int(time.time())
            lt_today = time.localtime(time.time())
            if lt[YEAR] == lt_today[YEAR] and \
                    lt[JULIANDAY] == lt_today[JULIANDAY]:
                self.m_today_stat = self.merge_stat_dicts(self.m_today_stat, D)
            if now - t < 60*60*24*7: # 7 days
                self.m_last7_stat = self.merge_stat_dicts(self.m_last7_stat, D)
            self.m_total_stat = self.merge_stat_dicts(self.m_total_stat, D)
    def get(self):
        """Will return a 0 <= value <= 1.0 that say how many percent is
        correct in this session
        """
        c = t = 0
        for k in self.m_session_stat.keys():
            if self.m_session_stat[k].has_key(k):
                c = c + self.m_session_stat[k][k]
            t = t + self.get_num_guess(self.m_session_stat, k)
        if t > 0:
            return 1.0 * c / t
        else:
            return 0.0
    def display(self):
        print
        v = self.m_session_stat.keys()
        v.sort()
        for x in v:
            print x, self.m_session_stat[x]
    def get_keys(self, all=0):
        """
        by default it returns the keys for all questions that have
        been asked. If 'all' is true, it also includes the keys for
        all the wrong answers.
        """
        keys = []
        for st in self.m_session_stat, self.m_today_stat, self.m_last7_stat, self.m_total_stat:
            for k in st.keys():
                if k not in keys:
                    keys.append(k)
                if all:
                    for wk in self.m_total_stat[k].keys():
                        if wk not in keys:
                            keys.append(wk)
        keys.sort()
        return keys
    def get_num_guess(self, st, key):
        if not st.has_key(key):
            return 0
        t = 0
        for i in st[key].keys():
            t = t + st[key][i]
        return t
    def get_percentage_correct(self, st, key):
        """
        This was added to be used for harmonic-interval.
        """
        if not st.has_key(key):
            return 0.0
        if st[key].has_key(key):
            num_correct = st[key][key]
        else:
            num_correct = 0
        total = 0
        for n in st[key].keys():
            total = total + st[key][n]
        return num_correct * 100.0 / total
    def key_to_pretty_name(self, k):
        return k
    def get_label_style(self):
        return 'normal'
    def reset_session(self):
        self.m_session_stat = {}


class Statistics(AbstractStatistics):
    def __init__(self, teacher):
        AbstractStatistics.__init__(self, teacher)
        self.load_statistics(self.m_savepath)
    def save_data(self):
        if not usable_statistics:
            return
        if self.m_session_stat:
            f = open(os.path.join(self.m_savepath, str(int(time.time()))), 'w')
            pickle.dump(self.m_session_stat, f)
            f.close()


class LessonStatistics(AbstractStatistics):
    def __init__(self, teacher):
        AbstractStatistics.__init__(self, teacher)
        self.m_cur_collection = self.m_cur_file = None
        self.lessonfile_changed(self.m_t.get_string('lessoncollection'),
                                self.m_t.get_string('lessonfile'))
    def get_label_style(self):
        return self.m_t.m_P.header.labelformat
    def lessonfile_changed(self, new_collection, new_file):
        if not new_file:
            return
        if self.m_cur_file and self.m_session_stat != {}:
            self.save_data()

        self.m_session_stat = {}
        self.m_today_stat = {}
        self.m_last7_stat = {}
        self.m_total_stat = {}
        self.m_cur_collection = new_collection
        self.m_cur_file = new_file
        if not usable_statistics:
            return
        self.create_statistics_dir()

        # if the lessonfile has changed, we have to
        # delete all statistics just to be save.
        if self.get_hash_of_statistics() != self.get_hash_of_lessonfile():
            p = os.path.join(self.m_savepath,
                             self.m_cur_collection,
                             self.m_cur_file)
            if os.path.isfile("%s_hash" % p):
                os.remove("%s_hash" % p)
            for f in os.listdir(p):
                os.remove(os.path.join(p, f))
        self.load_statistics(os.path.join(self.m_savepath,
                                          self.m_cur_collection,
                                          self.m_cur_file))
    def save_data(self):
        if not usable_statistics:
            return
        if self.m_session_stat == {}:
            return
        #save the hashvalue for the lessonfile this statistics was made with
        f = open(os.path.join(self.m_savepath,
                              self.m_cur_collection,
                              '%s_hash' % self.m_cur_file), 'w')
        f.write(str(self.get_hash_of_lessonfile()))
        f.close()
        f = open(os.path.join(self.m_savepath,
                              self.m_cur_collection,
                              self.m_cur_file,
                              str(int(time.time()))), 'w')
        pickle.dump(self.m_session_stat, f)
        f.close()
    def create_statistics_dir(self):
        dir = os.path.join(self.m_savepath, self.m_cur_collection)
        if not os.path.exists(dir):
            os.mkdir(dir)

        dir = os.path.join(dir, self.m_cur_file)
        if not os.path.exists(dir):
            os.mkdir(dir)
    def get_hash_of_statistics(self):
        # !wrong function name!
        # get the hash for the content of the lessonfile that was used last
        # time statistics was saved
        if os.path.isfile(os.path.join(self.m_savepath,
                                       self.m_cur_collection,
                                       '%s_hash' % self.m_cur_file)):
            s = open(os.path.join(self.m_savepath,
                                         self.m_cur_collection,
                                         '%s_hash' % self.m_cur_file)).read()
            try:
                return int(s)
            except:
                return 0
        else:
            return 0
    def get_hash_of_lessonfile(self):
        return hash(open(os.path.expanduser(os.path.join(
            self.m_t.get_string('lessoncollections/%s' % self.m_cur_collection),
            self.m_cur_file))).read())
    def key_to_pretty_name(self, key):
        for question in self.m_t.m_P.m_questions:
            if question.get_cname() == key:
                return question.get_name()
        return key

class IntervalStatistics(Statistics):
    def key_to_pretty_name(self, key):
        return utils.int_to_intervalname(key, 1, 1)

class HarmonicIntervalStatistics(Statistics):
    def key_to_pretty_name(self, key):
        return utils.int_to_intervalname(key, 1, 0)

if __name__ == '__main__':
    print "statistics_version:", get_statistics_version()
