on init
  declare const INACTIVE := 0
  declare const KEYSWITCH := 1
  declare const MIDICC := 2
  declare const VELOCITY := 3
  declare const SPEED := 4  
  declare const LEGATO := 5
  declare const NOTLEGATO := 6
  declare const REPETITION := 7
  declare const NOTREPETITION := 8
  declare const PEDALDOWN := 10
  declare const PEDALUP := 11  
  declare const TOGGLE := 12
  declare const KEYRANGE := 13
  
  declare const LOGIC_LASTOR := 0
  declare const LOGIC_ALLAND := 1  
  declare const LOGIC_MIDDLEOR := 2
  declare const NLOGIC := 3
    
  declare ui_label label1(1,1)
  declare ui_label label2(1,1)
  declare ui_label label3(2,1)
  declare ui_label label4(2,1)
  declare ui_menu logic
  declare ui_menu ks_duration
  declare ui_value_edit KS(-1,127,1)
  declare ui_button learn
  declare ui_value_edit min(0,100,1)
  declare ui_value_edit max(100,2000,1)
  declare last_time  
  declare last_note
  declare last_id
  declare bpm
  declare new_bpm
  declare condition  
  declare is_keyswitch
  declare is_outside_note_range
  declare settings[5]
  KS := -1
  min := 10
  max := 700
  
  declare ui_menu control1  
  declare ui_menu control2  
  declare ui_menu control3  
  declare ui_menu control4  
  declare ui_value_edit lower1(0,800,1)
  declare ui_value_edit lower2(0,800,1)
  declare ui_value_edit lower3(0,800,1)
  declare ui_value_edit lower4(0,800,1)
  declare ui_value_edit upper1(0,1500,1)  
  declare ui_value_edit upper2(0,1500,1)  
  declare ui_value_edit upper3(0,1500,1)  
  declare ui_value_edit upper4(0,1500,1)  
  declare ui_value_edit CC_KS1(0,127,1) 
  declare ui_value_edit CC_KS2(0,127,1) 
  declare ui_value_edit CC_KS3(0,127,1) 
  declare ui_value_edit CC_KS4(0,127,1) 
  declare ui_button learn1  
  declare ui_button learn2  
  declare ui_button learn3  
  declare ui_button learn4  
  declare ui_button active1
  declare ui_button active2
  declare ui_button active3
  declare ui_button active4
  declare learning_state1
  declare learning_state2
  declare learning_state3
  declare learning_state4    
  
  
  init_ui_row(control1, lower1, upper1, CC_KS1, learn1, learning_state1, active1, "", 1)
  init_ui_row(control2, lower2, upper2, CC_KS2, learn2, learning_state2, active2, "", 2)  
  init_ui_row(control3, lower3, upper3, CC_KS3, learn3, learning_state3, active3, "", 3)    
  init_ui_row(control4, lower4, upper4, CC_KS4, learn4, learning_state4, active4, "", 4)       
  
  add_menu_item(logic, "1&2&3&4", LOGIC_ALLAND)    
  add_menu_item(logic, "1&(2&3|4)", LOGIC_LASTOR)    
  add_menu_item(logic, "(1&2)|(3&4)", LOGIC_MIDDLEOR)    
  add_menu_item(logic, "not 1&2&3&4", NLOGIC + LOGIC_ALLAND)    
  add_menu_item(logic, "not 1&(2&3|4)", NLOGIC + LOGIC_LASTOR)    
  add_menu_item(logic, "not (1&2)|(3&4)", NLOGIC + LOGIC_MIDDLEOR)    
  set_text(label1, "Logic function:")
  set_text(label2, "KS active:")
  set_text(label3, "Articulation key switch: (or -1)")
  set_text(label4, "Valid speed range: (no change outside)")  
  add_menu_item(ks_duration, "Until next KS", 1)    
  add_menu_item(ks_duration, "While pressed (not yet implemented)", 2)    
  add_menu_item(ks_duration, "For one note (not yet implemented)", 3)    
        
  move_control(label1,1,5)  
  move_control(logic,1,6)  
  move_control(label2,2,5)  
  move_control(ks_duration,2,6)  
  move_control(label3,3,5)
  move_control(KS,3,6)  
  move_control(learn,4,6)       
  move_control(label4,5,5)
  move_control(min,5,6)
  move_control(max,6,6)
    
  make_persistent(logic)
  make_persistent(ks_duration)
  make_persistent(KS)
  make_persistent(min)
  make_persistent(max)
  
  SET_CONDITION(NO_SYS_SCRIPT_PEDAL)
end on

{ setup one condition row in the UI }
function init_ui_row(control, lower, upper, CC_KS, learn, learning_state, active, prepend_text, row)  
  add_menu_item(control, "------------", INACTIVE)
  add_menu_item(control, prepend_text & "Key switch", KEYSWITCH)  
  add_menu_item(control, prepend_text & "Velocity", VELOCITY)
  add_menu_item(control, prepend_text & "Speed(bpm)", SPEED)
  add_menu_item(control, prepend_text & "MIDI CC", MIDICC)
  add_menu_item(control, prepend_text & "Legato", LEGATO)
  add_menu_item(control, prepend_text & "Not legato", NOTLEGATO)
  add_menu_item(control, prepend_text & "Repetition", REPETITION)
  add_menu_item(control, prepend_text & "Not repetition", NOTREPETITION)
  add_menu_item(control, prepend_text & "Key range", KEYRANGE)
  add_menu_item(control, prepend_text & "Toggle", TOGGLE)
  add_menu_item(control, "-----------------------", INACTIVE)
  add_menu_item(control, ">> Pedal down", PEDALDOWN)
  add_menu_item(control, ">> Pedal up", PEDALUP)
  add_menu_item(control, "-----------------------", INACTIVE)
  add_menu_item(control, ">> MIDI CC (0-50)", 5000)
  add_menu_item(control, ">> MIDI CC (50-100)", 10050)  
  add_menu_item(control, "-----------------------", INACTIVE)
  add_menu_item(control, ">> MIDI CC (0-33)", 3300)
  add_menu_item(control, ">> MIDI CC (33-66)", 6633)
  add_menu_item(control, ">> MIDI CC (66-100)", 10066)
  add_menu_item(control, "-----------------------", INACTIVE)
  add_menu_item(control, ">> MIDI CC (0-25)", 2500)
  add_menu_item(control, ">> MIDI CC (25-50)", 5025)
  add_menu_item(control, ">> MIDI CC (50-75)", 7550)
  add_menu_item(control, ">> MIDI CC (75-100)", 10075)  
  add_menu_item(control, "-----------------------", INACTIVE)  
  add_menu_item(control, ">> MIDI CC (0-20)", 2000)
  add_menu_item(control, ">> MIDI CC (20-40)", 4020)
  add_menu_item(control, ">> MIDI CC (40-60)", 6020)
  add_menu_item(control, ">> MIDI CC (60-80)", 8060)
  add_menu_item(control, ">> MIDI CC (80-100)", 10080)      
  move_control(control,1,row)      
  move_control(lower,2,row)    
  move_control(upper,3,row)    
  move_control(CC_KS,4,row)
  move_control(learn,5,row)
  move_control(active,6,row)
  make_persistent(control)
  make_persistent(lower)
  make_persistent(upper)
  make_persistent(CC_KS)
  make_persistent(learn)
  make_persistent(active)
  CC_KS := 1
end function

{ called from the 'on note' callback when for some condition row when it's in learning mode }
function on_learning(learn, learning_state, control, lower, upper, CC_KS)
  if learn = 1
    select (control)
      case VELOCITY
        if learning_state = 1
          lower := 127
          upper := 0          
          learning_state := 0
        end if        
        if EVENT_VELOCITY < lower
          lower := EVENT_VELOCITY
        end if
        if EVENT_VELOCITY > upper
          upper := EVENT_VELOCITY
        end if
        
      case SPEED        
        if learning_state = 2
          lower := bpm
          upper := bpm                    
        else                  
          if learning_state > 2
            if bpm < lower
              lower := bpm
            end if
            if bpm > upper
              upper := bpm
            end if
          end if
        end if               
        inc(learning_state)
        
      case KEYSWITCH
        select(learning_state)
          case 1
            lower := EVENT_NOTE
          case 2
            upper := EVENT_NOTE
          case 3
            CC_KS := EVENT_NOTE
            learn := 0
        end select
        inc(learning_state)
        
      case KEYRANGE
        select(learning_state)
          case 1
            lower := EVENT_NOTE
          case 2
            upper := EVENT_NOTE
            learn := 0
        end select
        inc(learning_state)
        
    end select
  end if
end function

{ update the active state of a condition row }
function update_active(control, lower, upper, CC_KS, active)
  select(control)
    case INACTIVE
      active := 0
  
    case KEYSWITCH
      if in_range(EVENT_NOTE, lower, upper)
        is_keyswitch := 1
        if EVENT_NOTE = CC_KS
          active := 1
        else
          active := 0
        end if
      end if
      
    case VELOCITY
      if in_range(EVENT_VELOCITY, lower, upper)
        active := 1
      else
        active := 0
      end if
      
    case SPEED   
      if in_range(bpm, min, max)
        if in_range(bpm, lower, upper)
          active := 1
        else
          active := 0
        end if
      end if
      
    case MIDICC
      if in_range(CC[CC_KS], lower, upper)
        active := 1
      else
        active := 0
      end if  
      
    case LEGATO to NOTLEGATO
      if KEY_DOWN[last_note] = 1 and last_note # EVENT_NOTE
        active := 1
      else
        active := 0
      end if
      if control = NOTLEGATO
        if active=1 and KEY_DOWN[last_note]=1
          note_off(last_id)
        end if
        active := 1 - active { toggle }        
      end if
      
    case REPETITION to NOTREPETITION
      if EVENT_NOTE = last_note
        active := 1
      else
        active := 0
      end if
      if control = NOTREPETITION
        active := 1 - active { toggle }
      end if
      
    case KEYRANGE
      if in_range(EVENT_NOTE, lower, upper)
        active := 1
      else
        active := 0
        is_outside_note_range := 1
      end if
      
  end select
end function
 
on note  
  { check if in key switch learning mode }
  if learn = 1
    KS := EVENT_NOTE
    learn := 0
    exit
  end if
  
  { update bpm }
  new_bpm := 60000 / (ENGINE_UPTIME - last_time)  
  last_time := ENGINE_UPTIME
  if in_range(new_bpm, min, max)
    bpm := new_bpm        
  end if     
      
  { if we're in learning mode }
  if learn1+learn2+learn3+learn4 # 0
    on_learning(learn1, learning_state1, control1, lower1, upper1, CC_KS1)
    on_learning(learn2, learning_state2, control2, lower2, upper2, CC_KS2)
    on_learning(learn3, learning_state3, control3, lower3, upper3, CC_KS3)
    on_learning(learn4, learning_state4, control4, lower4, upper4, CC_KS4)
  else    
    is_keyswitch := 0 { this value might be set to true in one of the four functions below }
    is_outside_note_range := 0 { this value might be set to true in one of the four  functions below }
    update_active(control1, lower1, upper1, CC_KS1, active1)
    update_active(control2, lower2, upper2, CC_KS2, active2)
    update_active(control3, lower3, upper3, CC_KS3, active3)
    update_active(control4, lower4, upper4, CC_KS4, active4)
    
    { do the logical combination of the active buttons - 1 and (2 and 3 or 4) }
    condition := 1
    if logic mod NLOGIC = LOGIC_MIDDLEOR
      if control1 # INACTIVE and active1=0
        condition := 0
      end if
      if control2 # INACTIVE and active2=0
        condition := 0
      end if
      if condition = 0 and (control3 # INACTIVE or control4 # INACTIVE)
        condition := 1
        if control3 # INACTIVE and active3=0
          condition := 0
        end if
        if control4 # INACTIVE and active4=0
          condition := 0
        end if
      end if        
    else
      if control1 # INACTIVE and active1=0
        condition := 0
      else
        if control2 # INACTIVE and active2=0
          condition := 0
        end if
        if control3 # INACTIVE and active3=0
          condition := 0
        end if
        if logic mod NLOGIC = LOGIC_ALLAND
          if control4 # INACTIVE and active4=0
            condition := 0
          end if
        else
          if control4 # INACTIVE and active4=1
            condition := 1
          end if
        end if
      end if    
    end if    
    
    { if 'not' condition, negate the condition }
    if logic >= NLOGIC
      condition := 1 - condition
    end if
        
    if is_keyswitch = 1
      condition := 0                { block key switch notes }
    else if is_outside_note_range = 0
      last_note := EVENT_NOTE       { remember last note     }      
    end if       
    
    ignore_event(EVENT_ID)
    if condition = 1
      if KS # -1
        play_note(KS, 100, 0, -1)
      end if
      last_id := play_note(EVENT_NOTE, EVENT_VELOCITY, 0, -1)
    end if
  end if  
end on

{ ******************* on controller ******************* }
function on_controller(learn, learning_state, control, lower, upper, CC_KS)
  if learn = 1 and control = MIDICC
    if learning_state = 1
      lower := 127
      upper := 0
      learning_state := 0
    end if
    if CC[CC_KS] < lower
      lower := CC[CC_KS]
    end if
    if CC[CC_KS] > upper
      upper := CC[CC_KS]
    end if    
  end if
end function

on controller
  on_controller(learn1, learning_state1, control1, lower1, upper1, CC_KS1)
  on_controller(learn2, learning_state2, control2, lower2, upper2, CC_KS2)
  on_controller(learn3, learning_state3, control3, lower3, upper3, CC_KS3)
  on_controller(learn4, learning_state4, control4, lower4, upper4, CC_KS4)
end on

{ -------- GUI callbacks from here on ------------ }

{ ******************* on lower ******************* }
function on_lower(control, lower, upper)  
  if lower > upper
    upper := lower
  end if  
  if control # SPEED and lower > 127
    lower := 127
  end if
  if control # SPEED and upper > 127
    upper := 127
  end if
end function

on ui_control(lower1)
  on_lower(control1, lower1, upper1)
end on

on ui_control(lower2)
  on_lower(control2, lower2, upper2)
end on

on ui_control(lower3)
  on_lower(control3, lower3, upper3)
end on

on ui_control(lower4)
  on_lower(control4, lower4, upper4)
end on

{ ******************* on upper ******************* }
function on_upper(control, lower, upper)  
  if upper < lower
    lower := upper
  end if  
  if control # SPEED and lower > 127
    lower := 127
  end if
  if control # SPEED and upper > 127
    upper := 127
  end if
end function

on ui_control(upper1)
  on_upper(control1, lower1, upper1)
end on

on ui_control(upper2)
  on_upper(control2, lower2, upper2)
end on

on ui_control(upper3)
  on_upper(control3, lower3, upper3)
end on

on ui_control(upper4)
  on_upper(control4, lower4, upper4)
end on

{ ******************* on learn ******************* }
on ui_control(learn1)
  learning_state1 := 1    
end on

on ui_control(learn2)
  learning_state2 := 1    
end on

on ui_control(learn3)
  learning_state3 := 1    
end on

on ui_control(learn4)
  learning_state4 := 1    
end on

{ ******************* on control ******************* }
function on_control(control, CC_KS, lower, upper)
  if control > 100
    lower := 127 * (control mod 100) / 100 + 1
    upper := 127 * (control / 100) / 100
    if control mod 100 = 0
      lower := 0
    end if
    control := MIDICC        
  end if  
  if control = PEDALDOWN
    control := MIDICC    
    lower := 64
    upper := 127
    CC_KS := 64
  end if
  if control = PEDALUP
    control := MIDICC    
    lower := 0
    upper := 64
    CC_KS := 64
  end if  
end function

on ui_control(control1)
  on_control(control1, CC_KS1, lower1, upper1)
end on
  
on ui_control(control2)
  on_control(control2, CC_KS2, lower2, upper2)
end on

on ui_control(control3)
  on_control(control3, CC_KS3, lower3, upper3)
end on
  
on ui_control(control4)
  on_control(control4, CC_KS4, lower4, upper4)
end on