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


from __future__ import absolute_import

import os
import codecs
import logging
import pprint
import sys

from solfege import filesystem
from solfege.gpath import Path

import solfege

def get_learning_tree_list(debug):
    """
    Return a list of tuples with info about the learning trees found.
    Tuple objects:
    1. The string "user" or "solfege"
    2. The filename, not including the name of the directory
    3. Full path to the learning tree.
    """
    trees = []
    for cls, dir in (('solfege', u'learningtrees'),
        ('user', os.path.join(filesystem.user_data(), "learningtrees"))):
        try:
            v = os.listdir(dir)
        except OSError:
            v = []
        for fn in v:
            if cls == 'solfege':
                fullpath = os.path.join("learningtrees", fn)
            else:
                fullpath = os.path.join(filesystem.user_data(), "learningtrees", fn)
            trees.append((cls, fn, fullpath))
    if not debug:
        try:
            trees.remove(('solfege', 'debugtree.txt', os.path.join('learningtrees', 'debugtree.txt')))
        except ValueError:
            # The debugtree.txtfile is for some reason missing
            pass
    return trees

class _TreeCommon(list):
    def __init__(self, items=[]):
        if isinstance(items, _TreeCommon):
            list.__init__(self, [items])
        else:
            list.__init__(self, items)
    def dump(self, stream, level=0):
        print >> stream, "%s%s([" % (" " * level, self.__class__.__name__)
        self.dump_children(stream, level)
    def dump_children(self, stream, level):
        for child in self:
            if isinstance(child, _TreeCommon):
                child.dump(stream, level + 1)
            else:
                print >> stream, "%su'%s'," % (" " * (level + 1), child)
        if level == 0:
            print >> stream, "%s])" % (" " * level)
        else:
            print >> stream, "%s])," % (" " * level)
    def iterate_lesson_ids(self):
        """
        Yield the lesson id of each lesson that has been added to this
        object or its children.
        """
        for child in self:
            if isinstance(child, _TreeCommon):
                for c in child.iterate_lesson_ids():
                    yield c
            if isinstance(child, (str, unicode)):
                yield child
    def get_use_dict(self):
        """
        Return a dict where the keys are lesson_ids of lessons used
        in this objects and its children. The values are an integer
        value telling how many times it have been used.
        """
        retval = {}
        for lesson_id in self.iterate_lesson_ids():
            retval[lesson_id] = retval.get(lesson_id, 0) + 1
        return retval
    def iterate_topics_for_id(self, lesson_id):
        for child in self:
            if isinstance(child, (Page, Column, LinkList)):
                for c in child.iterate_topics_for_id(lesson_id):
                    yield c
            elif child == lesson_id:
                yield self.m_name
    def iterate_flattened(self):
        for child in self:
            yield child
            if not isinstance(child, unicode):
                for subchild in child.iterate_flattened():
                    yield subchild
    @staticmethod
    def tests_in_sub(sub):
        if isinstance(sub, unicode):
            try:
                return bool(solfege.lessonfile.manager.get(sub, 'test'))
            except KeyError:
                return False
        for child in sub:
            if _TreeCommon.tests_in_sub(child):
                return True
        return False


class _NamedTreeCommon(_TreeCommon):
    def __init__(self, name=u'', listitems=[]):
        assert isinstance(name, unicode)
        _TreeCommon.__init__(self, listitems)
        self.m_name = name 
    def dump(self, stream, level=0):
        print >> stream, "%s%s(_(u'%s'), [" % (" " * level, self.__class__.__name__, self.m_name)
        self.dump_children(stream, level)
        
class LinkList(_NamedTreeCommon):
    """
    A list of links leading to exercises or new pages.
    """
    def append(self, item):
        assert isinstance(item, (str, unicode, Page))
        super(LinkList, self).append(item)
    def __str__(self):
        return "LinkList(_('%s')# len: %i)" % (self.m_name, len(self))

class Column(_TreeCommon):
    def add_linklist(self, heading):
        self.append(LinkList(heading))
        return self[-1]
    def __str__(self):
        return "Column(#len: %i)" % len(self)

class Page(_NamedTreeCommon):
    def __init__(self, name=u'', listitems=[]):
        assert isinstance(name, unicode)
        _NamedTreeCommon.__init__(self, name, listitems)
        self.m_modified = False
    def __repr__(self):
        return "<Page name=%s>" % self.m_name
    def get(self, path):
        """
        Return the element pointed to by path.
        """
        elem = self
        for idx in path[1:]:
            elem = elem[idx]
        return elem
    def is_empty(self):
        """
        Return True if all columns are empty or only contain empty sections.
        """
        for col in self:
            for sect in col:
                if isinstance(sect, LinkList) and len(sect) != 0:
                    return False
        return True

class Paragraph(object):
    def __init__(self, text):
        self.m_text = text


def convert_old_learningtree_data(data):
    """
    fn is the filename of a learningtree file. It reads the file as
    the default format was for solfege 3.14.
    Returns the Page objects
    """
    p = Page(_('Untitled%s' % ''), [Column()])
    col = p[-1]
    for d in data['menu']:
        linklist = col.add_linklist(unicode(d['name']))
        for section in d['children']:
            assert isinstance(section, dict)
            # We create a new page with 1 col for each set of links
            subpage = Page(unicode(section['name']))
            subpage.append(Column())
            subcol = subpage[-1]
            sublinklist = subcol.add_linklist(unicode(section['name']))
            for child in section['children']:
                sublinklist.append(unicode(child))
            linklist.append(subpage)
    return p

def parse_tree(s, C_locale=False):
    """
    This function can load both new (3.15.2) and older learning trees.
    This function will convert old format to new format, and always return
    new format files.
    """
    if C_locale:
        def ifu(s):
            # We do the isinstance check because this way we can handle
            # people manually editing the data file and adding strings that
            # are plain ascii, but not marked as unicode ( u'' )
            return s if isinstance(s, unicode) else unicode(s)
        ifunc = ifu
    else:
        ifunc = _
    namespace = {
        'Page': Page,
        'Column': Column,
        'LinkList': LinkList,
        'Paragraph': Paragraph,
        '_': ifunc,
        '__builtins__': {},
    }
    ret = eval(s, namespace, namespace)
    if isinstance(ret, dict):
        name = ret['title']
        ret = convert_old_learningtree_data(ret)
        ret.m_name = name
    return ret

def load_tree(fn, C_locale=False):
    return parse_tree(codecs.open(fn, "rU", 'utf-8', 'replace').read())

