# -*- coding: utf-8 -*-
from functools import partial
from string import strip
from indentation import Indentation
from statement import Statements

def default_indentation():
    return 4 * ' '

class SpecificationProcessor:
    def __init__(self, file_name):
        self.file_name = file_name

        self._statements = Statements()

        self._test_found = False
        self._long_comment_found = False

    def process(self, lines):
        ret = []

        if len(filter(bool, map(strip, lines))) == 0:
            return ''

        # check file beginning now (attach defs as is)
        # this is a bit weak since it allows defs to be only at beginning
        defs = map(lambda a: a.startswith('def'), lines)

        def r_index(a, val):
            # missing list.rindex...
            return len(a) - a[::-1].index(val) - 1

        first_index = 0
        if any(defs):
            last_def_index = r_index(defs, True)

            try:
                first_index = map(lambda a: a.strip(), lines[last_def_index:]).index('')
            except ValueError:
                pass

            first_index += last_def_index

        processed_lines = map(lambda a: self.process_line(a), lines[:first_index])

        if len(processed_lines):
            ret.extend(['import unittest', 'import ' + self.file_name])
            
            ret.extend(processed_lines)

            test_class_name = 'Test' + self.file_name.capitalize()
            ret.append('class ' + test_class_name + '(unittest.TestCase):')

            new_lines, set_up = self.pick_set_up(lines[first_index:])
            ret.extend(filter(bool, map(partial(self.process_line, set_up=set_up), new_lines)))

            ret.extend(['suite = unittest.TestLoader().loadTestsFromTestCase(' + \
                test_class_name + ')',
                'unittest.TextTestRunner(verbosity=2).run(suite)'
            ])
        else:
            ret.extend(lines)

        return '\n'.join(ret)

    def pick_set_up(self, lines):
        found_set_up = False
        new_lines = []
        set_up = []

        for line in lines:
            if line[0] == ' ':
                if found_set_up:
                    set_up.append(line)
                else:
                    new_lines.append(line)
            else:
                stripped_line = line.strip()
                if len(stripped_line) > 0:
                    if stripped_line == 'set up':
                        found_set_up = True
                    else:
                        new_lines.append(line)
                elif found_set_up:
                    found_set_up = False
                else:
                    new_lines.append(line)

        return new_lines, set_up

    def process_line(self, line, set_up=()):
        stripped_line = line.strip()

        if len(stripped_line) > 0 and stripped_line[0] == '#':
            return line

        skips = ('def', 'return')
        if any(map(lambda a: stripped_line.startswith(a + ' '), skips)):
            return line

        just_found = False
        if stripped_line.endswith("'''"):
            self._long_comment_found = '=' in stripped_line
            just_found = True

            if not self._long_comment_found:
                return line

        if not just_found and self._long_comment_found:
            return line

        if line and line[0] == ' ':
            indentation = Indentation(line)
            ret = self._statements.convert(stripped_line)

            if hasattr(ret, '__iter__'):
                return default_indentation() + indentation() + ret[0] + \
                    '\n' + default_indentation() + indentation() + ret[1]

            return (default_indentation() + indentation() + ret).rstrip()

        if len(stripped_line) > 0 and not self._test_found:
            self._test_found = True

            ret = '\n' + default_indentation() + 'def test_' + \
                stripped_line.replace(' ', '_') + '(self):'

            for content in set_up:
                ret += '\n' + default_indentation() + content

            return ret

        if not stripped_line or stripped_line == '\n':
            self._test_found = False

        return (default_indentation() + line).rstrip()

@@ -545,127 +545,169 @@
   # 
-check file beginning now (attach defs as is)%0A        # this is a bit weak since it allows defs to be only at beginning%0A
+note that this assumes defs and assignments are at the beginning,%0A        # possible set up next and actual tests after that%0A        def first_test_index():%0A    
     
@@ -759,25 +759,28 @@
  lines)%0A
-%0A
+    
         
 def r_in
@@ -775,496 +775,360 @@
     
-def r_index(a, val):%0A            # missing list.rindex...%0A            return len(a) - a%5B::-1%5D.index(val) - 1%0A%0A        first_index = 0%0A        if any(defs):%0A            last_def_index = r_index(defs, True)%0A%0A            try:%0A                first_index = map(lambda a: a.strip(), lines%5Blast_def_index:%5D).index('')%0A            except ValueError:%0A                pass%0A%0A            first_index += last_def_index%0A%0A        processed_lines = map(lambda a: self.process_line(a), lines%5B:first_index
+last_def = defs%5B-1%5D if len(defs) else 0%0A%0A            for i, line in enumerate(lines%5Blast_def + 1:%5D):%0A                if len(line) and not line.startswith(' '):%0A                    return last_def + 1 + i%0A%0A            return len(lines)%0A%0A        i = first_test_index()%0A%0A        ret.extend(lines%5B:i%5D)%0A%0A        new_lines, set_up = self.pick_set_up(lines%5Bi:
 %5D)%0A%0A
@@ -1142,25 +1142,19 @@
  if len(
-processed
+new
 _lines):
@@ -1229,61 +1229,8 @@
 me%5D)
-%0A            %0A            ret.extend(processed_lines)
 %0A%0A  
@@ -1375,78 +1375,8 @@
 ')%0A%0A
-            new_lines, set_up = self.pick_set_up(lines%5Bfirst_index:%5D)%0A
     
@@ -1674,52 +1674,8 @@
   %5D)
-%0A        else:%0A            ret.extend(lines)
 %0A%0A  

Clean up implementation