// Router - Pipeline for processing input through stages
var time = use('time')

// Valid emacs keys
var valid_emacs_keys = [
  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  'return', 'enter', 'space', 'escape', 'tab', 'backspace', 'delete',
  'up', 'down', 'left', 'right', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
]

var emacs_special = {
  'return': 'RET', 'enter': 'RET', 'space': 'SPC', 'escape': 'ESC',
  'tab': 'TAB', 'backspace': 'DEL', 'delete': 'delete'
}

// Gesture stage - detects swipes and pinches from touchpad
function gesture_stage(config) {
  config = config || {}
  var min_swipe = config.swipe_min_dist || 30
  var max_time = config.swipe_max_time || 500
  var pinch_th = config.pinch_threshold || 10
  
  var touches = {}
  var gesture_state = null
  var start_dist = 0
  
  function dist(p1, p2) {
    var dx = p2[0] - p1[0]
    var dy = p2[1] - p1[1]
    return Math.sqrt(dx * dx + dy * dy)
  }
  
  return {
    process: function(events) {
      var output = []
      
      for (var i = 0; i < length(events); i++) {
        var ev = events[i]
        
        // Only process gamepad touchpad events
        if (ev.control != 'gamepad_touchpad') {
          push(output, ev)
          continue
        }
        
        var fingerId = (ev.touchpad || 0) + '_' + (ev.finger || 0)
        
        if (ev.pressed) {
          touches[fingerId] = {
            pos: ev.pos,
            startPos: ev.pos,
            startTime: time.number()
          }
          
          var count = length(array(touches))
          if (count == 1) gesture_state = 'single'
          else if (count == 2) {
            gesture_state = 'multi'
            var fingers = Object.values(touches)
            start_dist = dist(fingers[0].pos, fingers[1].pos)
          }
        }
        else if (ev.kind == 'axis') {
          if (touches[fingerId]) {
            touches[fingerId].pos = ev.pos
            
            var count = length(array(touches))
            if (count == 2 && gesture_state == 'multi') {
              var fingers = Object.values(touches)
              var currentDist = dist(fingers[0].pos, fingers[1].pos)
              var d = currentDist - start_dist
              
              if (Math.abs(d) >= pinch_th / 100) {
                push(output, {
                  kind: 'gesture',
                  device_id: ev.device_id,
                  control: d > 0 ? 'pinch_out' : 'pinch_in',
                  pressed: true,
                  released: false,
                  delta: d,
                  time: ev.time
                })
                start_dist = currentDist
              }
            }
          }
        }
        else if (ev.released) {
          if (touches[fingerId]) {
            var touch = touches[fingerId]
            var count = length(array(touches))
            
            if (count == 1 && gesture_state == 'single') {
              var dt = (time.number() - touch.startTime) * 1000
              var dx = ev.pos[0] - touch.startPos[0]
              var dy = ev.pos[1] - touch.startPos[1]
              
              if (dt < max_time) {
                var absX = Math.abs(dx), absY = Math.abs(dy)
                if (absX > min_swipe / 100 || absY > min_swipe / 100) {
                  var dir = absX > absY
                    ? (dx > 0 ? 'swipe_right' : 'swipe_left')
                    : (dy > 0 ? 'swipe_down' : 'swipe_up')
                  
                  push(output, {
                    kind: 'gesture',
                    device_id: ev.device_id,
                    control: dir,
                    pressed: true,
                    released: false,
                    time: ev.time
                  })
                }
              }
            }
            
            delete touches[fingerId]
            if (length(array(touches)) == 0) gesture_state = null
          }
        }
      }
      
      return output
    }
  }
}

// Emacs stage - converts keyboard input to emacs chords
function emacs_stage() {
  var prefix = null
  
  return {
    process: function(events) {
      var output = []
      
      for (var i = 0; i < length(events); i++) {
        var ev = events[i]
        
        // Only process keyboard button events
        if (ev.device_id != 'kbm' || ev.kind != 'button' || !ev.pressed) {
          push(output, ev)
          continue
        }
        
        if (find(valid_emacs_keys, ev.control) == null) {
          push(output, ev)
          continue
        }
        
        // Only process if we have modifiers OR waiting for chord
        if (!ev.mods?.ctrl && !ev.mods?.alt && !prefix) {
          push(output, ev)
          continue
        }
        
        var notation = ""
        if (ev.mods?.ctrl) notation += "C-"
        if (ev.mods?.alt) notation += "M-"
        
        if (length(ev.control) == 1) {
          notation += lower(ev.control)
        } else {
          notation += emacs_special[ev.control] || ev.control
        }
        
        // Handle prefix keys
        if (notation == "C-x" || notation == "C-c") {
          prefix = notation
          continue  // Consume, don't output
        }
        
        // Complete chord if we have prefix
        if (prefix) {
          var chord = prefix + " " + notation
          prefix = null
          push(output, {
            kind: 'chord',
            device_id: ev.device_id,
            control: chord,
            pressed: true,
            released: false,
            time: ev.time
          })
        } else {
          push(output, {
            kind: 'chord',
            device_id: ev.device_id,
            control: notation,
            pressed: true,
            released: false,
            time: ev.time
          })
        }
      }
      
      return output
    }
  }
}

// Action mapping stage - converts controls to named actions
function action_stage(bindings) {
  var down = {}
  
  return {
    down: down,
    process: function(events) {
      var output = []
      
      for (var i = 0; i < length(events); i++) {
        var ev = events[i]
        
        // Pass through non-button events
        if (ev.kind != 'button' && ev.kind != 'chord' && ev.kind != 'gesture') {
          push(output, ev)
          continue
        }
        
        var actions = bindings.get_actions(ev.control)
        
        if (length(actions) == 0) {
          push(output, ev)
          continue
        }
        
        for (var j = 0; j < length(actions); j++) {
          var action = actions[j]
          
          if (ev.pressed) down[action] = true
          else if (ev.released) down[action] = false
          
          push(output, {
            kind: 'action',
            device_id: ev.device_id,
            control: action,
            pressed: ev.pressed,
            released: ev.released,
            time: ev.time,
            original: ev
          })
        }
      }
      
      return output
    }
  }
}

// Delivery stage - dispatches to possessed entity
function delivery_stage(user) {
  return {
    process: function(events) {
      for (var i = 0; i < length(events); i++) {
        var ev = events[i]
        
        // Only deliver actions
        if (ev.kind != 'action') continue
        
        user.dispatch(ev.control, {
          pressed: ev.pressed,
          released: ev.released,
          time: ev.time
        })
      }
      
      return events
    }
  }
}

// Create router with pipeline
function make(user, config) {
  config = config || {}
  
  var stages = []
  var action = null
  
  if (config.gestures != false) {
    push(stages, gesture_stage(config))
  }
  
  if (config.emacs != false) {
    push(stages, emacs_stage())
  }
  
  action = action_stage(user.bindings)
  push(stages, action)
  push(stages, delivery_stage(user))
  
  return {
    stages: stages,
    
    get down() { return action.down },
    
    handle: function(canon) {
      var events = [canon]
      
      for (var i = 0; i < length(this.stages); i++) {
        events = this.stages[i].process(events)
      }
      
      return events
    }
  }
}

return {
  make: make,
  gesture_stage: gesture_stage,
  emacs_stage: emacs_stage,
  action_stage: action_stage,
  delivery_stage: delivery_stage
}
