# This file is part of python-ly, https://pypi.python.org/pypi/python-ly
#
# Copyright (c) 2008 - 2015 by Wilbert Berendsen
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# See http://www.gnu.org/licenses/ for more information.

"""
Add, check or remove bar checks in selected music.
"""

from __future__ import unicode_literals
from __future__ import print_function

import collections
import itertools

import ly.document
import ly.lex.lilypond


def remove(cursor):
    """Remove bar checks from the selected music."""
    s = ly.document.Source(cursor, tokens_with_position=True)
    prv, cur = None, None
    with cursor.document as d:
        for nxt in itertools.chain(s, (None,)):
            if isinstance(cur, ly.lex.lilypond.PipeSymbol):
                if isinstance(prv, ly.lex.Space):
                    # pipesymbol and adjacent space may be deleted
                    if nxt == '\n':
                        del d[prv.pos:cur.end]
                    elif isinstance(nxt, ly.lex.Space):
                        del d[cur.pos:nxt.end]
                    else:
                        del d[cur.pos:cur.end]
                elif isinstance(nxt, ly.lex.Space):
                    # delete if followed by a space 
                    del d[cur.pos:cur.end]
                else:
                    # replace "|" with a space
                    d[cur.pos:cur.end] = " "
            prv, cur = cur, nxt


class event(object):
    """A limited event type at a certain time."""
    def __init__(self):
        self._nodes = []
        self.cadenza = None
        self.barcheck = False
        self.timesig = None
        self.partial = None
    
    def append(self, node):
        self._nodes.append(node)

    def __repr__(self):
        s = []
        if self.cadenza is not None:
            s.append('cadenza' + ('On' if self.cadenza else 'Off'))
        if self.barcheck:
            s.append('bar')
        if self.timesig is not None:
            s.append('T{0}'.format(self.timesig))
        if self.partial is not None:
            s.append('P{0}'.format(self.partial))
        if self._nodes:
            s.append(repr(self._nodes))
        return '<event {0}>'.format(' '.join(s))


def insert(cursor, music=None):
    """Insert bar checks within the selected range."""
    if music is None:
        import ly.music
        music = ly.music.document(cursor.document)
    
    if len(music) == 0:
        return
    
    if cursor.start:
        n = music.node(cursor.start, 1)
        nodes = itertools.chain((n,), n.forward())
    else:
        nodes = music
    if cursor.end is None:
        iter_nodes = iter
    else:
        predicate = lambda node: node.position < cursor.end
        def iter_nodes(it):
            return itertools.takewhile(predicate, it)
    
    # make time-based lists of events
    event_lists = []
    
    def do_topnode(node):
        if not isinstance(node, ly.music.items.Music):
            for n in node:
                do_topnode(n)
            return
        
        def do_node(node, time, scaling):
            if isinstance(node, (ly.music.items.Durable, ly.music.items.UserCommand)):
                if node.position >= cursor.start:
                    events[time].append(node)
                time += node.length() * scaling
            elif isinstance(node, ly.music.items.TimeSignature):
                events[time].timesig = node.measure_length()
            elif isinstance(node, ly.music.items.Partial):
                events[time].partial = node.length()
            elif isinstance(node, ly.music.items.PipeSymbol):
                events[time].barcheck = True
            elif isinstance(node, ly.music.items.Command) and node.token in (
                    'cadenzaOn', 'cadenzaOff'):
                events[time].cadenza = node.token == 'cadenzaOn'
            elif isinstance(node, ly.music.items.Grace):
                pass
            elif isinstance(node, ly.music.items.LyricMode):
                pass
            elif isinstance(node, ly.music.items.MusicList) and node.simultaneous:
                time = max(do_node(n, time, scaling) for n in iter_nodes(node))
            elif isinstance(node, ly.music.items.Music):
                if isinstance(node, ly.music.items.Scaler):
                    scaling *= node.scaling
                for n in iter_nodes(node):
                    time = do_node(n, time, scaling)
            else:
                do_topnode(node)
            return time
        
        events = collections.defaultdict(event)
        do_node(node, 0, 1)
        event_lists.append(sorted(events.items()))
    
    do_topnode(nodes)
    
    for event_list in event_lists:
        
        # default to 4/4 without pickup
        measure_length = 1
        measure_pos = 0
        
        for time, evt in event_list:
            print(time, evt)


