# Setup, don't modify this
import re
import collections
from pprint import pprint

stencils = collections.OrderedDict()
Patches_Per_Show = 16 # so 1st knob is shows/stencil, 2nd is patch

def make():

    marker = "Generated by ipython patchGenerate"
    output = []
    output.append("// %s:\n" % marker)

    patch_names = []
    for a_stencil in stencils.values():
        output.extend( a_stencil.patch_code_lines() )
    
    # patches[]
    patch_decl = []
    for a_stencil in stencils.values():
        the_patch_names = a_stencil.patch_names()
        # fill out the 16/patch
        for i in range(len(the_patch_names),16):
            the_patch_names.append("empty_patch")

        patch_decl.extend(["patch_%s" % x for x in the_patch_names])

    output.append(
        "const byte** patches[] = {"
        + ", ".join(patch_decl)
        + "};"
        )
    output.append("")

    i=0
    name_list = []
    output.append('const char empty_patch_name[] PROGMEM = "-";')
    for a_stencil in stencils.values():
        the_patch_names = a_stencil.patch_names()
        
        for n in the_patch_names :
            output.append("const char patch_name_%s[] PROGMEM = \"%s\";" % (i,n))
            name_list.append("patch_name_%s" % i)
            i += 1
        
        for empty_i in range(len(the_patch_names),16):
            name_list.append("empty_patch_name")
        
    output.append(
        "PROGMEM char* const patch_names[] = {"
        + ", ".join(name_list)
        + "};"
        )
    
    output.append("const byte Patch_Count = count_of(patches);")
    output.append("// Generated by ipython patchGenerate")
    print "\n".join(output)


"""
Explanation: Generate patch.h for Dan's Chromcord Arduino
The first block is just setup, don't modify it (execute it once):
End of explanation
"""


# for use by "with"
class new_stencil(object):
        current = None
        
        def __init__(self,name):
            self.name = name
            self.patches = collections.OrderedDict()
            new_stencil.current = self
        
        def __enter__(self):
            pass
            
        def __exit__(self,err_type,err_value,traceback):
            # print "exited %s w/%s" % (self,len(self.patches))
            stencils[self.name] = self

        def patch_names(self):
            x=[]
            for a_patch in self.patches.values():
                x.append(a_patch.token_name())
            return x
        
        def patch_code_lines(self):
            lines = []
            for a_patch in self.patches.values():
                lines.extend( a_patch.code_lines())
                lines.append("")
            return lines   
        
class new_patch(object):
        current = None

        def __init__(self,name):
            self.name = name
            self.zones = []
            new_patch.current = self
            self.stencil = new_stencil.current


        def __enter__(self):
            pass
            
        def __exit__(self,err_type,err_value,traceback):
            self.stencil.patches[self.name] = self
            # print "exited %s w/%s" % (self,len(self.zones))

        def token_name(self):
            n = "%s_%s" % (self.stencil.name,self.name)
            n = re.sub('[^a-z0-9_]', '', n, flags=re.IGNORECASE)
            return n
        
        def code_lines(self):
            lines = [ "// Patch '%s' : '%s'" % (self.stencil.name, self.name) ]
            
            zone_maps = []
            for i,zone in enumerate(self.zones):
                if (zone and len(zone)>0):
                    zone_maps.append("patch_%s_z%s" % (self.token_name(),i))
                    array_lit = "{%s,-1}" % ",".join(("%s" % lunit) for lunit in zone)
                    lines.append( 
                        "    const byte patch_%s_z%s[] = %s;"
                        % (
                            self.token_name(), 
                            i,
                            array_lit
                            )
                        )
                else:
                    zone_maps.append('empty_zone')
                    

            zone_lit = ''
            if len(zone_maps) > 0:
                zone_lit = "{%s}" % ", ".join(zone_maps)
            else:
                zone_lit = 'empty_zones'
                
            lines.append(
                "const byte* patch_%s[Zone_Count] = %s;"
                % (
                    self.token_name(), 
                    zone_lit
                    )
                )

            return lines
                
def assign_zone(*lunit_list):
    # print "make zone %s for %s of %s" % (lunit_list, new_patch.current, new_stencil.current)
    if len(lunit_list) and isinstance(lunit_list[0],list):
        new_patch.current.zones.append(lunit_list[0])
    else:
        new_patch.current.zones.append(lunit_list)

"""
Explanation: the dsl support (execute once)
End of explanation
"""


# Edit, and Run to check


"""
Explanation: Patches
Patches are structured as:
1. Group Name, like "Show 1 
1. Patch name, Like "Squarish #2 
1. A list of the Zones (rgb controllers), 1..4
1. Each lunit that belongs to the zone
E.g.:
```
with new_stencil('Show 1'):
    with new_patch('squarish'):
        assign_zone(0,1,2,3,4,5)
        assign_zone(6,7,8)
        assign_zone(range(9,16))
        assign_zone()
    with new_patch('center'):
        assign_zone(0,1,2,3,5,8,9,11,12,13,14,15)
        assign_zone(4,7,10)
        assign_zone()
        assign_zone()
    with new_patch('squarish #2'):
        assign_zone(0,1,2,3,4,5)
        assign_zone(6,7,8)
        assign_zone(range(9,16))
        assign_zone([])
with new_stencil('Show 2'):
    with new_patch('cross'):
        assign_zone(0,1,2,3,5,8,9,11,12,13,14,15)
        assign_zone([])
        assign_zone([])
        assign_zone(4,7,10)```
Keeps the order you specify.
End of explanation
"""


# Run this and paste result into patches.h
#------------------------------------------------
# 2016-10-26_RiverBend
with new_stencil('Test'):
    with new_patch('Test-00'):
        assign_zone(0,5,10)
        assign_zone(1,6,11) 
        assign_zone(2,7,12)
        assign_zone(3,8,13)       
    with new_patch('Test-01'):
        assign_zone(0,1,2,3,4)
        assign_zone(5,6,8,9) 
        assign_zone(10,11,13,14)
        assign_zone(7,12)     
    with new_patch('Test-02'):
        assign_zone(4,9,14)
        assign_zone(11,12,13) 
        assign_zone(4,9,14) 
        assign_zone(1,2,5,8,10)
    with new_patch('Test-03'):
        assign_zone(4,9,14) 
        assign_zone(11,12,13) 
        assign_zone(1,2,3,6,7,8) 
        assign_zone(0,5,10)
        
with new_stencil('Tester'):        
    with new_patch('Tester-01'):
        assign_zone(1,3,8,13,14) 
        assign_zone(4,5,9,10,11) 
        assign_zone(0,6) 
        assign_zone(2,7,12)
    with new_patch('Tester-02'):
        assign_zone(3,8,13,14)
        assign_zone(5,7,10,11,12)
        assign_zone(6,2,9)
        assign_zone(0,1,4)
    with new_patch('Tester-03'):
        assign_zone(1,3,5,8,13)
        assign_zone(0,2,7,10,11,12)
        assign_zone(6,14)
        assign_zone(4,9)   
    with new_patch('Tester-04'):
        assign_zone(1,5,8,13)
        assign_zone(0,3,4,7,9,12)
        assign_zone(6)
        assign_zone(2,10,11,14)
        
with new_stencil('Tester2'):           
        
    with new_patch('Tester2-01'):
        assign_zone(1,3,5,8,13,14)
        assign_zone(0,4,7,9,10,11,12) 
        assign_zone(6)
        assign_zone(2)
    with new_patch('Tester2-02'):
        assign_zone(1,5,8,13)
        assign_zone(0,3,7,12)
        assign_zone(4,6,10,14)
        assign_zone(2,9,11)
    with new_patch('Tester2-03'):
        assign_zone(1,3,5,8,13)
        assign_zone(0,7,12)
        assign_zone(4,6,10,14)
        assign_zone(2,9,11)
    with new_patch('Tester2-04'):
        assign_zone(1,3,8,13)
        assign_zone(0,4,7,9,11,12)
        assign_zone(6,14)
        assign_zone(2,5,10)  



#-----------------------------------------

make()

"""
Explanation: Run this to make the C++ code:
End of explanation
"""


