import os
import sys
import struct
import time
import unittest

sys.path = ["./py"] + sys.path

import cbox

global Document
Document = cbox.Document

def verify_region(region, has, has_not, full = False):
    values = set()
    values_dict = {}
    rtext = region.as_string() if not full else region.as_string_full()
    for pair in rtext.split(" "):
        if "=" in pair:
            key, value = pair.split("=")
            values.add(pair)
            values.add(key)
            values_dict[key] = value
    if 'genericmod' in rtext:
        print (rtext, "Expected:", has)
        assert False
    for i in has:
        if '_oncc' in i:
            i = i.replace('_oncc', '_cc')
        if i not in values:
            print("Not found: %s, has: %s" % (i, rtext))
            assert False
    for i in has_not:
        if i in values:
            print("Found unwanted string: %s=%s" % (i, values_dict[i]))
            assert False

scene = Document.get_scene()
scene.clear()
instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument()

npfs = instrument.engine.load_patch_from_string(0, '.', '', 'new_patch')
instrument.engine.set_patch(1, 0)

glb = npfs.get_global()
assert glb
assert glb.get_children()
master = glb.get_children()[0]

g1 = master.new_child()
g1.set_param("cutoff", "100")
g1.set_param("resonance", "6")
g1.set_param("fil_type", "lpf_4p")
g1.set_param("fileg_decay", "0.2")
g1.set_param("fileg_sustain", "10")
g1.set_param("fileg_depth", "5400")
g1.set_param("fileg_release", "10")
g1.set_param("ampeg_release", "0.1")
g1.set_param("amp_veltrack", "0")
g1.set_param("volume", "-12")
g1.set_param("fileg_depthcc14", "-5400")

def check_exception(param, value, substr):
    error = False
    try:
        g1.set_param(param, value)
    except Exception as e:
        error = True
        assert substr in str(e), "Exception with substring '%s' expected when setting %s to %s, caught another one: %s" % (substr, param, value, str(e))
    assert error, "Exception with substring '%s' expected when setting %s to %s, none caught" % (substr, param, value)

check_exception("cutoff", "bla", "correct numeric value")
check_exception("key", "bla", "valid note name")
check_exception("lochan", "bla", "correct integer value")
check_exception("lochan", "10.5", "correct integer value")
check_exception("offset", "bla", "correct unsigned integer value")
check_exception("offset", "10.5", "correct unsigned integer value")
check_exception("offset", "-1000", "correct unsigned integer value")

# Make sure that multiple CC assignments work
g1.set_param("locc5", "100")
g1.set_param("locc8", "100")
g1.set_param("hicc8", "110")

#g1.set_param("cutoff", "1000")
#g1.set_param("fillfo_freq", "4")
#g1.set_param("fillfo_depth", "2400")
#g1.set_param("fillfo_wave", "12")

r1 = g1.new_child()
r1.set_param("sample", "*saw")
r1.set_param("transpose", "0")
r1.set_param("tune", "5")
r1.set_param("gain_cc17", "12")

verify_region(g1, ["locc5=100", "locc8=100", "hicc8=110"], ['hicc5'])
verify_region(r1, ["locc5=100", "locc8=100", "hicc8=110"], [], full=True)

r1.set_param("hicc5", "120")
r1.set_param("hicc8", "124")

verify_region(g1, ["locc5=100", "locc8=100", "hicc8=110"], ['hicc5'])
verify_region(g1, ["locc5=100", "hicc5=127", "locc8=100", "hicc8=110"], [], full=True)
verify_region(r1, ["locc5=100", "hicc5=120", "locc8=100", "hicc8=124"], [], full=True)

r2 = g1.new_child()
r2.set_param("sample", "*sqr")
r2.set_param("transpose", "12")
r2.set_param("gain_cc17", "-12")
verify_region(r2, ["sample=*sqr", "gain_cc17=-12", "transpose=12"], [])
r2.set_param("gain_cc17", "-24")
verify_region(r2, ["gain_cc17=-24"], ["gain_cc17=-12"])
r2.unset_param("gain_cc17")
verify_region(r2, ["sample=*sqr"], ["gain_cc17"])
r2.unset_param("transpose")
verify_region(r2, ["sample=*sqr"], ["transpose"])
r2.unset_param("sample")
verify_region(r2, [], ["transpose", "sample"])

g1.unset_param("cutoff")
g1.unset_param("resonance")
g1.unset_param("fil_type")
g1.unset_param("fileg_sustain")
g1.unset_param("fileg_decay")
g1.unset_param("fileg_depth")
g1.unset_param("fileg_release")
g1.unset_param("ampeg_release")
g1.unset_param("amp_veltrack")
g1.unset_param("volume")
g1.unset_param("fileg_depthcc14")
g1.unset_param("locc5")
g1.unset_param("locc8")
g1.unset_param("hicc8")

params_to_test = [
    'lokey', 'hikey', 'lovel', 'hivel', 'key',
    'cutoff', 'pan', 'offset', 'tune', 'position', 'width',
    'amp_random', 'fil_random', 'pitch_random', 'delay_random',
    'pitch_veltrack', 'reloffset_veltrack', 'offset_veltrack',
    'delay_cc5', 'delay_cc10', 'reloffset_cc5', 'reloffset_cc10', 'offset_cc5', 'offset_cc10',
    'delay_curvecc8', 'reloffset_curvecc5', 'offset_curvecc10',
    'delay_stepcc8', 'reloffset_stepcc5', 'offset_stepcc10',
    'cutoff_cc1', "resonance_cc1", 'pitch_cc1', 'tonectl_cc1', 'gain_cc1', 'amplitude_cc1',
    'cutoff_oncc2', "resonance_oncc2", 'pitch_oncc2', 'tonectl_oncc2', 'gain_oncc2', 'amplitude_oncc2',
    'cutoff_curvecc1', 'resonance_curvecc5', 'pitch_curvecc10', 'amplitude_curvecc10',
    'cutoff_smoothcc1', 'resonance_smoothcc5', 'pitch_smoothcc10', 'amplitude_smoothcc10',
    'cutoff_stepcc1', 'resonance_stepcc5', 'pitch_stepcc10', 'amplitude_stepcc10',
    'loop_start', 'loop_end',
    'ampeg_attack',
    'amplfo_depth', 'fillfo_depth',
    'fileg_vel2depth',
    'fileg_depthcc5', 'fillfo_depthcc8','amplfo_depthcc5',
    'fileg_depth_curvecc5', 'fillfo_depth_curvecc8','amplfo_depth_curvecc5',
    'fileg_depth_smoothcc5', 'fillfo_depth_smoothcc8','amplfo_depth_smoothcc5',
    'fileg_depth_stepcc5', 'fillfo_depth_stepcc8','amplfo_depth_stepcc5',
    'amplfo_freqcc5', 'fillfo_freqcc10', 'pitchlfo_freqcc5',
    'cutoff_chanaft', 'resonance_chanaft',
    'cutoff_polyaft', 'resonance_polyaft',
    'amplfo_depthpolyaft', 'fillfo_depthpolyaft', 'pitchlfo_depthpolyaft', 
    'amplfo_freqpolyaft', 'fillfo_freqpolyaft', 'pitchlfo_freqpolyaft',
    'eq1_freqcc1', 'eq2_gaincc2', 'eq3_bwcc3',
    'eq1_freq_curvecc1', 'eq2_gain_curvecc2', 'eq3_bw_curvecc3',
    'eq1_freq_smoothcc1', 'eq2_gain_smoothcc2', 'eq3_bw_smoothcc3',
    'eq1_freq_stepcc1', 'eq2_gain_stepcc2', 'eq3_bw_stepcc3',
    'fileg_vel2start', 'fileg_vel2delay', 'fileg_vel2attack', 'fileg_vel2hold', 'fileg_vel2decay', 'fileg_vel2sustain', 'fileg_vel2release',
    'fileg_startcc1', 'fileg_delaycc1', 'fileg_attackcc1', 'fileg_holdcc1', 'fileg_decaycc1', 'fileg_sustaincc1', 'fileg_releasecc1',
    'fileg_start_curvecc1', 'fileg_delay_curvecc1', 'fileg_attack_curvecc1', 'fileg_hold_curvecc1', 'fileg_decay_curvecc1', 'fileg_sustain_curvecc1', 'fileg_release_curvecc1',
    'fileg_start_smoothcc1', 'fileg_delay_smoothcc1', 'fileg_attack_smoothcc1', 'fileg_hold_smoothcc1', 'fileg_decay_smoothcc1', 'fileg_sustain_smoothcc1', 'fileg_release_smoothcc1',
    'fileg_start_stepcc1', 'fileg_delay_stepcc1', 'fileg_attack_stepcc1', 'fileg_hold_stepcc1', 'fileg_decay_stepcc1', 'fileg_sustain_stepcc1', 'fileg_release_stepcc1',
    'cutoff2', 'resonance2', 'fileg_depth2', 'fileg_depth2cc1', 'fileg_depth2_stepcc1',
    'fillfo_depth2', 'fillfo_depth2cc1', 'fillfo_depth2_stepcc1',
    'amp_velcurve_5', 'amp_velcurve_127',
    'locc5', 'hicc5',
    'on_locc8', 'on_hicc8',
    'lfo5_freq', 'lfo1_wave', 'lfo3_fade', 'lfo4_delay',
    ]
for i in range(len(params_to_test)):
    param = params_to_test[0]
    print ("Trying %s" % param)
    rest = params_to_test[1:]
    value1, value2 = "100", "80"
    if 'key' in param:
        value1, value2 = "e1", "g1"
    # Verify that a setting is reported back
    r2.set_param(param, value1)
    verify_region(r2, ["%s=%s" % (param, value1)], rest)

    # Verify that setting the same value in parent doesn't change the local 'has' flag
    g1.set_param(param, value1)
    verify_region(r2, ["%s=%s" % (param, value1)], rest)

    # Verify that setting a different local value doesn't get overridden by parent
    r2.set_param(param, value2)
    verify_region(r2, ["%s=%s" % (param, value2)], rest)
    # Write the original value
    r2.set_param(param, value1)
    verify_region(r2, ["%s=%s" % (param, value1)], rest)

    # Delete the parent value, confirm the deletion doesn't propagate to child
    g1.unset_param(param)
    verify_region(r2, ["%s=%s" % (param, value1)], rest)
    # Set a different value in the parent, confirm it doesn't affect the child
    g1.set_param(param, value2)
    verify_region(g1, ["%s=%s" % (param, value2)], rest)
    verify_region(r2, ["%s=%s" % (param, value1)], rest)

    # Check that the newly created child inherits the setting from the parent
    r3 = g1.new_child()
    verify_region(r3, [], [param])
    verify_region(r3, ["%s=%s" % (param, value2)], [], full=True)
    r3.delete()

    # Verify that the original child still has the original value
    verify_region(r2, ["%s=%s" % (param, value1)], [], full=True)
    # Delete the child value, make sure it disappears but the inherited
    # value is still reported in the full listing
    r2.unset_param(param)
    verify_region(r2, [], params_to_test)
    verify_region(r2, ["%s=%s" % (param, value2)], [], full=True)
    # Delete the setting in the parent, make sure the inherited value in the
    # child disappears too
    g1.unset_param(param)
    verify_region(r2, [], ["%s=%s" % (param, value2)], full=True)

    master.set_param(param, value1)
    verify_region(master, ["%s=%s" % (param, value1)], [])
    verify_region(g1, [], params_to_test)
    verify_region(g1, ["%s=%s" % (param, value1)], [], full=True)
    verify_region(r2, [], params_to_test)
    verify_region(r2, ["%s=%s" % (param, value1)], [], full=True)
    master.unset_param(param)
    verify_region(master, [], params_to_test)
    verify_region(g1, [], params_to_test)
    verify_region(r2, [], params_to_test)
    
    params_to_test = params_to_test[1:] + params_to_test[0:1]

old_names = [
    ("hilev", "hivel"),
    ("lolev", "lovel"),
    ("loopstart", "loop_start"),
    ("loopend", "loop_end"),
    ("loopmode", "loop_mode", "one_shot", "loop_continuous"),
    ("bendup", "bend_up"),
    ("benddown", "bend_down"),
    ("offby", "off_by"),
]

for t in old_names:
    if len(t) == 2:
        old, new = t
        v1, v2 = "10", "20"
    else:
        old, new, v1, v2 = t
    print ("Trying alias: %s" % old)
    r1.set_param(old, v1)
    verify_region(r1, ["%s=%s" % (new, v1)], [old])
    r1.set_param(old, v2)
    verify_region(r1, ["%s=%s" % (new, v2)], [old])
    r1.unset_param(old)
    verify_region(r1, [], [old, new])
    r1.set_param(new, v1)
    verify_region(r1, ["%s=%s" % (new, v1)], [old])
    r1.set_param(new, v2)
    verify_region(r1, ["%s=%s" % (new, v2)], [old])
    r1.unset_param(new)
    verify_region(r1, [], [old, new])
