#!/usr/bin/python3

def cb_dummy(options={}):
    print("cb_dummy",options)
    return options

def ValueBuffer_create_data(val=2,_min=0,_max=255,_inc=1,cycle=[0,127,255]):
    data = {}
    data["value"] = val
    data["init_value"] = val
    data["state"] = 1
    data["inc"] = _inc
    data["min"] = _min
    data["max"] = _max
    data["cycle"] = cycle
    data["cycle_idx"] = 0
    data["format"] = "<format>{}"
    data["name"] = "<name>"
    data["callback"] = cb_dummy
    return data

def Buffer_clean(data,default={}):
    #print( "ValueBuffer_clean(data)" )
    a = ValueBuffer_create_data()
    #for k,v in data.items():
    #    print("  ",id(data),[k,v])
    for k,v in a.items():
        #print("  ",id(data),[k,v])
        if k not in data:
            print("   ValueBuffer_clear key does not exist; adding key ",[k]," !")
            data[k] = v

def ValueBuffer_clean(data):
    default=ValueBuffer_create_data()
    return Buffer_clean(data,default)

def ValueBuffer_check(data):
    ValueBuffer_clean(data)
    #print(":::",data["value"],data["min"],data["max"])
    if data["value"] < data["min"]:
        data["value"] = data["min"]
        print("MIN !")
    if data["value"] > data["max"]:
        data["value"] = data["max"]
        print("MAX !")

def ValueBuffer_cycle(data): # middel mouse click
    ValueBuffer_clean(data)
    _len = len(data["cycle"])
    idx=data["cycle_idx"]
    if idx >= _len:
        idx = _len-1
    else:
        idx += 1
    data["cycle_idx"]=idx
    data["value"] = data["cycle"][idx]
    ValueBuffer_check(data)
    return data["value"]

def ValueBuffer_dec(data,value=None): #Mouse Wheel down
    ValueBuffer_clean(data)
    if value is None:
        value = data["inc"]
    if type(value) is float:
        value = round(value,4)
    data["value"] -= value
    ValueBuffer_check(data)
    return data["value"]

def ValueBuffer_inc(data,value=None): #Mouse Wheel up
    ValueBuffer_clean(data)
    if value is None:
        value = data["inc"]
    if type(value) is float:
        value = round(value,4)
    data["value"] += value
    ValueBuffer_check(data)
    return data["value"]

def ValueBuffer_val(data,value=None): # get or set value
    ValueBuffer_clean(data)
    if value is None:
        return float(data["value"])

    if type(value) is float:
        data["value"] = round(value,4)
    data["value"] = value
    ValueBuffer_check(data)
    return float(data["value"])

def ValueBuffer_reset(data): # right click
    ValueBuffer_clean(data)
    data["value"] = data["init_value"]
    ValueBuffer_check(data)
    return 1

def ValueBuffer_on(data): # left click
    ValueBuffer_clean(data)
    data["state"] = 1

def ValueBuffer_off(data): # left click
    ValueBuffer_clean(data)
    data["state"] = 0

def ValueBuffer_toggle(data):
    ValueBuffer_clean(data)
    v = data["state"]
    if v:v = 0
    else:v = 1
    data["state"] = v
    return v
    
def ValueBuffer_invert(data):
    ValueBuffer_clean(data)
    data["value"] *=-1
    ValueBuffer_check(data)

def ValueBuffer_is(data):
    ValueBuffer_clean(data)
    if data["state"]:
        return 1
    return 0

def ValueBuffer_format(data):
    ValueBuffer_clean(data)
    fm = data["format"]
    print("ValueBuffer_format", fm,data)
    try:
        if "{val" in fm and "{state" in fm:
            return fm.format(val=data["value"],state=data["state"])
        if "{val" in fm:
            return fm.format(val=data["value"])
        if "{state" in fm:
            return fm.format(state=data["state"])
        return 
    except Exception as e:
        print(fm,data)
        raise e


# warp function to object
class ValueBuffer():
    def __init__(self,val=2,_min=0,_max=255,_inc=1,cycle=[0,127,255]):
        self.data = ValueBuffer_create_data(val=val,_min=_min,_max=_max,_inc=_inc,cycle=cycle)
    def cycle(self): # middel mouse click
        return ValueBuffer_cycle(self.data)
    def check(self):
        return ValueBuffer_check(self.data)
    def dec(self,value=None): #Mouse Wheel down
        return ValueBuffer_dec(self.data,value)
    def inc(self,value=None): #Mouse Wheel up
        return ValueBuffer_inc(self.data,value)
    def val(self,value=None): # get or set value
        return ValueBuffer_val(self.data,value)
    def reset(self): # right click
        return ValueBuffer_reset(self.data)
    def on(self): # left click
        return ValueBuffer_on(self.data)
    def off(self): # left click
        return ValueBuffer_off(self.data)
    def toggle(self):
        return ValueBuffer_toggle(self.data)
    def invert(self):
        return ValueBuffer_invert(self.data)
    def _is(self):
        return ValueBuffer_is(self.data)
    def format(self):
        return ValueBuffer_format(self.data)


def create_default_FadeBuffer():
    return ValueBuffer(val=2,_min=0,_max=20,_inc=0.1)  

def create_default_DelayBuffer():
    return ValueBuffer(val=0.04,_min=-1,_max=1,_inc=0.01,cycle=[0.1,0.2,0.3,0.4,0.5])  


FADE = create_default_FadeBuffer() 
FADE.data["name"]   = "FADE"
FADE.data["format"] = "FADE:\n{val:0.2f}"

DELAY = create_default_DelayBuffer()
DELAY.data["name"]   = "DELAY"
DELAY.data["format"] = "DELAY:\n{val:0.2f}"
DELAY.off()

FADE_move = create_default_FadeBuffer() 
FADE_move.data["name"]   = "FADE_move"
FADE_move.data["format"] = "PAN/TILT\nFADE:{val:0.2f}"
FADE_move.data["init_value"] = 4.0
FADE_move.reset()

FADE_move_delay = create_default_DelayBuffer()
FADE_move.data["name"]   = "FADE_move_delay"
FADE_move.data["format"] = "PAN/TILT\nD:{val:0.2f}"

def OptionBuffer_create_data(val="",options=None):
    if not options:
        options = ["option1","option2"]
    data = {}
    data["options"]  = options
    data["init_idx"] = 0
    data["idx"]      = 0
    data["wrap"]     = 0 #start from begining
    
    OptionBuffer_set_init(data,val)
    return data

def OptionBuffer_set_init(data,val):
    idx=0
    if val in data:
        idx = data.index(val)
    data["init_idx"] = idx


def ValueBuffer_clean(data):
    default=OptionBuffer_create_data()
    return Buffer_clean(data,default)

def OptionBuffer_check(data):
    _len = len(data["options"]) 
    if data["wrap"]:
        if data["idx"] >= _len:
            data["idx"] = 0
        if data["idx"] < 0:
            data["idx"] = _len-1 
    else: 
        if data["idx"] >= _len:
            data["idx"] = _len-1
        if data["idx"] < 0:
            data["idx"] = 0

    
def OptionBuffer_reset(data):
    ValueBuffer_clean(data)
    data["idx"] = data["init_idx"]
    OptionBuffer_check(data)

def OptionBuffer_inc(data):
    ValueBuffer_clean(data)
    data["idx"] += 1
    OptionBuffer_check(data)

def OptionBuffer_dec(data):
    ValueBuffer_clean(data)
    data["idx"] -= 1
    OptionBuffer_check(data)

def OptionBuffer_val(data,val=None):
    ValueBuffer_clean(data)
    OptionBuffer_check(data)
    #val = "RGB"
    if val in data["options"]:
        print("set",val)
        idx = data["options"].index(val)
        data["idx"] = idx
    idx= data["idx"]
    return data["options"][idx]

class OptionBuffer():# wrap function to object
    def __init__(self,val="",options=[]):
        self.data = OptionBuffer_create_data(val="",options=options)
        #self._rep = self.__repr__()
    def reset(self):
        return OptionBuffer_reset(self.data)
    def inc(self):
        return OptionBuffer_inc(self.data)
    def __repr__(self):
        return "<lib.meta.OptionBuffer {} '{}'>".format(id(self),self.val())
    def dec(self):
        return OptionBuffer_dec(self.data)
    def val(self,val=None):
        #self.data["callback"]({"val":val})
        return OptionBuffer_val(self.data,val=val)
    def set_init(self,val):
        return OptionBuffer_set_init(self.data,val)

class Elem_Container():
    def __init__(self):
        #self.commands = []
        self.labels = []
        self.val = {}
        self.elem = {}

ATTR_GRP = {}
ATTR_GRP[" "] = [" "]
ATTR_GRP["POS"] = ["PAN","TILT"]
ATTR_GRP["RGB"] = ["RED","GREEN","BLUE","AMBER","WHITE"]
#ATTR_GRP["CMY"] = ["CYAN","MAGENTA","YELLOW"]
ATTR_GRP["BEAM"] = ["DIM","FOCUS","ZOOM","IRIS"] # GOBO1

def Optionbuffer_create_ATTR_GRP(val=""):
    options = list(ATTR_GRP.keys())
    return OptionBuffer(val,options)

FX_FUNC = ["SIN","COS","RAMP","RAMP2","FD","ON","STATIC","-","255"]
def Optionbuffer_create_FX_FUNC(val=""):
    options = FX_FUNC
    return OptionBuffer(val,options)

def Optionbuffer_create_ATTR(attr_grp="",val=""):
    options = ["none1","none2"]
    if attr_grp in ATTR_GRP:
        options = ATTR_GRP[attr_grp]
    #options.append(" ")
    if " " not in options:
        options.insert(0," ")
    ob = OptionBuffer(val,options)
    #ob.set_init(" ")
    ob.reset()
    return ob

#fx_func_grp = Optionbuffer_create_ATTR_GRP()
#fx_func_attr = Optionbuffer_create_ATTR("POS")
#fx_func_func = Optionbuffer_create_FX_FUNC()

fx_prm_main = {}
fx_prm_move = {"SIZE":40,"SPEED":8,"OFFSET":100,"BASE":"0","START":0,"MODE":0,"MO":0,"DIR":1,"INVERT":0,"SHUFFLE":0,"WING":2,"WIDTH":100}

live_prm = Elem_Container()
live_prm.labels = ["FADE","DELAY","PAN/TILT\nFADE","PAN/TILT\nDELAY","-","-"]
live_prm.labels = ["FADE","DELAY","PAN/TILT\nFADE","-","-","-"]

#fx_color = {"A":"red","B":"blue"} 
fx_prm = {"SIZE":255,"SPEED":10,"OFFSET":100,"BASE":"-","START":0,"MODE":0,"MO":0,"DIR":1,"INVERT":1,"SHUFFLE":0,"WING":2,"WIDTH":25,"2D-X":1,"2D:MODE":0}
fx_x_modes = ["spiral","left","right","up","down","left_right","up_down"]

fx_modes = ["RED","GREEN","BLUE","MAG","YELLOW","CYAN"]
fx_mo    = ["fade","on","rnd","ramp","ramp2","cosinus","sinus","static"]





# MASTER --------
setup = Elem_Container()
#setup.labels = ["SAVE\nSHOW","LOAD\nSHOW","NEW\nSHOW","SAVE\nSHOW AS","SAVE &\nRESTART","DRAW\nGUI","PRO\nMODE"]
setup.labels = ["SAVE\nSHOW","LOAD\nSHOW","NEW\nSHOW","SAVE\nSHOW AS","SAVE &\nRESTART","PRO\nMODE"]

fx_main = Elem_Container()
fx_main.labels =["REC-FX","FX OFF","\n"]
fx_moves = Elem_Container()
fx_moves.labels =[
        "FX:CIR","FX:PAN","FX:TILT", "WIDTH:\n100","DIR:\n0","INVERT:\n0","\n",
        "SHUFFLE:\n0","SIZE:\n","SPEED:\n","START:\n","OFFSET:\n","\n"
        ]
fx3 = Elem_Container()
fx3_prm = {}
fx3_grid = {}
#{"SIZE":40,"SPEED":8,"OFFSET":100,"BASE":"0","START":0,"MODE":0,"MO":0,"DIR":1,"INVERT":0,"WING":2,"WIDTH":100}
fx3_grid_cfg = {}
fx3_grid_cfg["GRP:"]   = Optionbuffer_create_ATTR_GRP
fx3_grid_cfg["ATTR:"]  = Optionbuffer_create_ATTR #("POS")
fx3_grid_cfg["TYPE:"]  = Optionbuffer_create_FX_FUNC #()
fx3_grid_cfg["WIDTH:"] = ValueBuffer #(val=2,_min=0,_max=20,_inc=0.1)  
fx3_grid_cfg["SIZE:"]   = ValueBuffer 
fx3_grid_cfg["SPEED:"]  = ValueBuffer 
fx3_grid_cfg["OFFSET:"] = ValueBuffer 
fx3_grid_cfg["START:"]  = ValueBuffer 
fx3_grid_cfg["SHUFFLE:"] = ValueBuffer 
fx3_grid_cfg["DIR:"]     = ValueBuffer 
fx3_grid_cfg["INVERT:"]  = ValueBuffer 
fx3_grid_cfg["WING:"]  = ValueBuffer 
fx3_grid_cfg["BASE:"]  = ValueBuffer 

fx3.labels =[] 
for i,f in fx3_grid_cfg.items():
    x=[]
    x.append(i)
    for j in range(4):
        k="{}{}".format(i,j+1)
        x.append(k)
        fx3_grid[k] = f()
        fx3_grid[k].data["name"] = k #f()

    x.append("\n")
    print(":",x)
    fx3.labels.extend(x)

def change_grp(name,val):
    t,i = name.split(":",1)
    print("--- change_grp",name,val,t,i)
    k2 = "ATTR:"+i
    print("change_grp:",(name,val),(t,i),k2,val)

    fx3_grid[k2]  = Optionbuffer_create_ATTR(val) #,val)
    fx3_grid[k2].data["name"] = "ATTR:"+i
    #fx3_grid[k2].val(val) 
    print("   ",k2,fx3_grid[k2].data) 


#print("__ "*22)
#fx3_grid["GRP:1"].val("RGB")
#print([fx3_grid["GRP:1"].val()])
#print("_ _ "*22)
#fx3_grid["ATTR:1"].val("PAN")
#print([fx3_grid["ATTR:1"].val()])
#print("__ "*22)

fx3.labels.extend([" "," "," "," "," ","\n"])
fx3.labels.extend(["\n"])
fx3.labels.extend([" ","START","STOP","OFF"," ","\n"])
fx3.labels.extend(["  ","CIRCLE","PAN","TILT","FLAY","\n"])
fx3.labels.extend([" ","RED","GREEN","BLUE","MAGENTA","\n"])
fx3.labels.extend(["SIZE:","MINI","SMALL","NORMAL","BIG","\n"])
fx3.labels.extend(["SPEED:","SLOW","MID","FAST","TURBO","\n"])

fx_cfg = Elem_Container()
fx_cfg.labels =[
        "FX:DIM"," ", "WIDTH:\n25","WING:\n2","DIR:\n1","INVERT:\n1","\n","SHUFFLE:\n0"
        ,"SIZE:\n","SPEED:\n","START:\n","OFFSET:\n","BASE:\n-","2D-X:\n-","2D:MODE"
        ]
fx_generic = Elem_Container()
fx_generic.labels =["FX:SIN","FX:COS","FX:RAMP","FX:RAMP2","FX:FD","FX:ON","FX:STATIC"]

fx_color = Elem_Container()
fx_color.labels =["FX:RED","FX-C:A","FX-C:B"] 

commands = Elem_Container()
commands.labels =["\n","ESC","CFG-BTN","LABEL","-","DEL","-","\n"
        ,"SELECT","FLASH","GO","-","MOVE","S-KEY","\n"
        ,"BLIND","CLEAR","REC","EDIT","COPY",".","\n" 
        ]