
import string, sys, random
import gnoetics
import fragment

class Template:

    def __init__(self, *fragments):
        self.__fragments = list(fragments)

    def length(self):
        return len(self.__fragments)

    def get(self, i):
        return self.__fragments[i]

    def append(self, f):
        self.__fragments.append(f)

    def is_finished(self):
        for f in self.__fragments:
            if not (f.is_bound() or f.is_blank()):
                return 0
        return 1

    def completed_syllables(self):
        tally = 0
        for f in self.__fragments:
            if f.is_bound():
                tally += f.get_syllables()
        return tally

    def get_metric_error(self):
        tally = 0
        for f in self.__fragments:
            tally += f.get_metric_error()

        N = len(self.__fragments)

       
        for i in xrange(N):

            a = (i > 0 and self.__fragments[i-1]) or None
            b = self.__fragments[i]
            c = (i < N-1 and self.__fragments[i+1]) or None

            # check for widows and orphans

            if (not a or a.is_break()) \
               and b.is_text()         \
               and c and c.is_stop()   \
               and b.get_syllables() < 3:
                tally += 3.1 - b.get_syllables()

            if a and a.is_start()                \
               and b.is_text()             \
               and (not c or c.is_break()) \
               and b.get_syllables() < 3:
                tally += 3.1 - b.get_syllables()


            # in general, we want to end a sentence
            # on a stress

            if a and a.is_text() and b.is_stop():
                m1 = a.get_token().meter()
                if m1 and m1[-1] == 'u':
                    tally += 0.25
                
        return tally
    

    def pretty_print(self, show_meter=0, show_pos=1):
        pp = ""
        last_was_start = 0
        for f in self.__fragments:
            if f.is_break():
                pp += "\n"
            elif not f.is_bound():
                pp += "(?) "
            else:
                t = f.get_token()
                if t.is_start():
                    last_was_start = 1
                elif t.is_stop():
                    pp += ". "
                    last_was_start = 0
                else:
                    s = t.to_string()
                    if last_was_start:
                        s = s.capitalize()
                    pp += " " + s
                    last_was_start = 0

        ppl = pp.split("\n")
        for line in ppl:
            print line.strip()

        if show_meter:
            for f in self.__fragments:
                if f.is_text():
                    print f.get_token().meter(),
                elif f.is_break():
                    print
            print

        if show_pos:
            for f in self.__fragments:
                if f.is_text():
                    print gnoetics.pos_mask_to_string(f.get_token().pos_mask()),
                elif f.is_break():
                    print
            print

        print


    def next_fragment(self):
        next = -1
        next_priority = 0
        for i in xrange(self.length()):
            f = self.get(i)
            p = f.get_priority()
            if p > next_priority:
                next = i
                next_priority = p

        return next

    
    def solve(self, markov, max_metric_error=-1):

        state = self
        history = [state]

        while 1:

            pos = state.next_fragment()
            # If there is no next fragment, we must be finished.
            if pos < 0:
                break

            f = state.get(pos)
            state = f.evolve(markov, state, pos)

            if not state \
               or (max_metric_error >= 0 and \
                   state.get_metric_error() > max_metric_error):
                N = len(history)
                if N < 2:
                    print "shit!"
                    # tough luck
                    return None
                else:
                    back = random.randint(1, N-1)
                    history = history[:back]
                    state = history[-1]

            else:
                history.append(state)

        return state
    

    def spew(self):

        for i in xrange(self.length()):
            f = self.get(i)
            print i,
            if not f.is_break():
                if f.is_bound():
                    print f.get_token().to_string(),
                else:
                    print "(unbound)",
            print
        print
        print
                    
                
    
