// Action mapping system that sits at the root of the scene tree
// Consumes raw input and reissues as named actions

var io = use('cellfs')
var input = use('input')
var json = use('json')

var action = {}

var controller_map = {
  ps3: 'playstation',
  ps4: 'playstation',
  ps5: 'playstation',
  xbox: 'xbox360',
  xbox: 'xboxone',
  switch: 'switchpro',
  xbox: 'standard',
  gamecube: 'gamecube',
  switch: 'joyconleft',
  switch: 'joyconright',
  switch: 'joyconpair'
}

action.get_icon_for_action = function(action)
{
  var bindings = this.get_bindings_for_device(action)
  if (!length(bindings)) return null

  var primary_binding = bindings[0]
  var button = null
  var key_mapping = {
    'escape': 'escape',
    'return': 'return',
    'space': 'space',
    'up': 'arrow_up',
    'down': 'arrow_down',
    'left': 'arrow_left',
    'right': 'arrow_right'
  }
  var key = null
  var controller_prefix = null
  var gamepad_mapping = null
  var icon_name = null

  if (this.current_device == 'keyboard') {
    if (starts_with(primary_binding, 'mouse_button_')) {
      button = replace(primary_binding, 'mouse_button_', '')
      return 'ui/mouse/mouse_' + button + '.png'
    } else {
      // Handle special keyboard keys
      key = key_mapping[primary_binding] || primary_binding
      return 'ui/keyboard/keyboard_' + key + '.png'
    }
  }

  if (this.current_device == 'gamepad' && this.current_gamepad_type) {
    controller_prefix = controller_map[this.current_gamepad_type] || 'playstation'

    // Map gamepad inputs to icon names
    gamepad_mapping = {
      'gamepad_a': this.current_gamepad_type == 'ps5' || this.current_gamepad_type == 'ps4' || this.current_gamepad_type == 'ps3' ? 'playstation_button_cross' : 'xbox_button_a',
      'gamepad_b': this.current_gamepad_type == 'ps5' || this.current_gamepad_type == 'ps4' || this.current_gamepad_type == 'ps3' ? 'playstation_button_circle' : 'xbox_button_b',
      'gamepad_x': this.current_gamepad_type == 'ps5' || this.current_gamepad_type == 'ps4' || this.current_gamepad_type == 'ps3' ? 'playstation_button_square' : 'xbox_button_x',
      'gamepad_y': this.current_gamepad_type == 'ps5' || this.current_gamepad_type == 'ps4' || this.current_gamepad_type == 'ps3' ? 'playstation_button_triangle' : 'xbox_button_y',
      'gamepad_dpup': controller_prefix + '_dpad_up',
      'gamepad_dpdown': controller_prefix + '_dpad_down',
      'gamepad_dpleft': controller_prefix + '_dpad_left',
      'gamepad_dpright': controller_prefix + '_dpad_right',
      'gamepad_l1': controller_prefix + '_trigger_l1',
      'gamepad_r1': controller_prefix + '_trigger_r1',
      'gamepad_l2': controller_prefix + '_trigger_l2',
      'gamepad_r2': controller_prefix + '_trigger_r2',
      'gamepad_start': this.get_start_button_icon()
    }

    icon_name = gamepad_mapping[primary_binding]
    if (icon_name) {
      return 'ui/' + controller_prefix + '/' + icon_name + '.png'
    }
  }

  return null
}

action.get_start_button_icon = function() {
  if (this.current_gamepad_type == 'ps3') {
    return 'playstation3_button_start'
  } else if (this.current_gamepad_type == 'ps4') {
    return 'playstation4_button_options'
  } else if (this.current_gamepad_type == 'ps5') {
    return 'playstation5_button_options'
  } else {
    return 'xbox_button_start'
  }
}

var default_action_map = {
  'move_up': ['w', 'gamepad_dpup', 'swipe_up'],
  'move_left': ['a', 'gamepad_dpleft', 'swipe_left'],  
  'move_down': ['s', 'gamepad_dpdown', 'swipe_down'],
  'move_right': ['d', 'gamepad_dpright', 'swipe_right'],
  'accio': ['space', 'gamepad_x'], 
  'reset': ['r', 'gamepad_y'],
  'undo': ['z'],
  'menu': ['escape', 'gamepad_start'],
  
  'ui_up': ['w', 'up', 'gamepad_dpup'],
  'ui_down': ['s', 'down', 'gamepad_dpdown'],
  'ui_left': ['a', 'left', 'gamepad_dpleft'],
  'ui_right': ['d', 'right', 'gamepad_dpright'],
  
  'confirm': ['return', 'space', 'mouse_button_left', 'gamepad_b'], 
  'cancel': ['escape', 'gamepad_a'], 
  
  // Editor controls
  'brush_left': ['gamepad_dpleft', 'left'],
  'brush_right': ['gamepad_dpright', 'right'],
  'tool_up': ['gamepad_dpup', 'up'],    
  'tool_down': ['gamepad_dpdown', 'down'],
  'tool_use': ['gamepad_a', 'mouse_button_left'],
  'editor_undo': [',', 'gamepad_l1'],
  'editor_redo': ['.', 'gamepad_r1'],
  'editor_inspect': ['C-p'],
  'editor_marquee': ['mouse_button_right', 'gamepad_r2'],
  'editor_erase': ['x', 'gamepad_b'],
  'editor_eyedrop': ['i', 'gamepad_x'],
  'editor_grab': ['mouse_button_middle', 'gamepad_y'],
  'editor_ccw': ['gamepad_l1', 'q'],
  'editor_cw': ['gamepad_r1', 'e'],
}

default_action_map.witch_up = 'up'
default_action_map.witch_down = 'down'
default_action_map.witch_left = 'left'
default_action_map.witch_right = 'right'
default_action_map.accion = 'y'

// Utility to detect device from input id
function detect_device(input_id) {
  if (starts_with(input_id, 'gamepad_')) return 'gamepad'
  if (starts_with(input_id, 'swipe_')) return 'touch'
  if (starts_with(input_id, 'mouse_button')) return 'keyboard'  // Mouse buttons are part of keyboard/mouse controller
  if (starts_with(input_id, 'touch_')) return 'touch'
  return 'keyboard'
}

// Display names for actions
var action_display_names = {
  'move_up': 'Move Up',
  'move_down': 'Move Down',
  'move_left': 'Move Left',
  'move_right': 'Move Right',
  'ui_up': 'UI Up',
  'ui_down': 'UI Down',
  'ui_left': 'UI Left',
  'ui_right': 'UI Right',
  'menu': 'Menu',
  'confirm': 'Confirm',
  'cancel': 'Cancel',
  'reset': 'Reset',
  'accio': 'Summon',
  'brush_left': 'Previous Entity',
  'brush_right': 'Next Entity',
  'tool_up': 'Previous Tool',
  'tool_down': 'Next Tool',
  'tool_use': 'Use Tool'
}

action.down = {}
action.action_map = {}
action.display_names = action_display_names
action.is_rebinding = false
action.rebind_target = null
action.current_device = 'keyboard'
action.current_gamepad_type = null

// Copy defaults
arrfor(array(default_action_map), function(key) {
  action.action_map[key] = array(default_action_map[key])
})

// Swipe‐recognizer state & tuning
var swipe = { x0:null, y0:null, t0:0 }
var SWIPE_MIN_DIST = 30    // pixels
var SWIPE_MAX_TIME = 500   // ms
action.on_input = function(action_id, evt)
{
  // 1) Detect & store which device user is on (only from raw input, ignore passive inputs)
  var new_device = null
  var is_real_kb_mouse = false
  var gamepad_type = null
  if (action_id != 'mouse_move' && !starts_with(action_id, 'mouse_pos') && evt.pressed) {
    new_device = detect_device(action_id)

    // For keyboard/mouse detection, also check if the event has modifier keys or is a mouse button
    // This helps distinguish real keyboard/mouse events from mapped actions
    is_real_kb_mouse = (new_device == 'keyboard' &&
                        (evt.ctrl != null || evt.shift != null || evt.alt != null ||
                         starts_with(action_id, 'mouse_button')))

    if (new_device == 'keyboard' && !is_real_kb_mouse) {
      // This might be a mapped action, not a real keyboard/mouse event
      return
    }

    if (new_device != this.current_device) {
      if (new_device == 'gamepad') {
        gamepad_type = evt.which != null ? input.gamepad_id_to_type(evt.which) : 'unknown'
        log.console("Input switched to 'gamepad' (event: " + action_id + ", type: " + gamepad_type + ")")
        this.current_gamepad_type = gamepad_type
      } else if (this.current_device == 'gamepad') {
        log.console("Input switched to 'keyboard/mouse' (event: " + action_id + ")")
      }
      this.current_device = new_device
    }
  }
  
  // 2) If we're in rebind mode, grab the raw input
  if (this.is_rebinding) {
    if (evt.pressed) {
      // Only bind if it's from the same device type
      if (detect_device(action_id) == this.current_device) {
        this.rebind_action(this.rebind_target, action_id)
        this.save_bindings()
      }
      // Exit rebind mode
      this.is_rebinding = false
      this.rebind_target = null
    }
    return // Don't also fire the mapped action
  }
  
  // 3) Otherwise, find all mapped actions for this input
  var matched_actions = []
  arrfor(array(this.action_map), mapped_action => {
    if (find(this.action_map[mapped_action], action_id) != null) {
      push(matched_actions, mapped_action)
      
      if (evt.pressed)
        this.down[mapped_action] = true
      else if (evt.released)
        this.down[mapped_action] = false
    }
  })
  
  // Send all matched actions (only if we found mappings - this means it's a raw input)
  var i = 0
  if (length(matched_actions) > 0) {
    for (i = 0; i < length(matched_actions); i++) {
//      scene.recurse(game.root, 'on_input', [matched_actions[i], evt])
    }
  }
}

action.start_rebind = function(action_name) {
  if (!this.action_map[action_name]) return
  this.is_rebinding = true
  this.rebind_target = action_name
}

action.rebind_action = function(action_name, new_key) {
  if (!this.action_map[action_name]) return
  
  // Remove this key from all other actions
  arrfor(array(this.action_map), act => {
    var idx = find(this.action_map[act], new_key)
    if (idx != null)
      this.action_map[act] = array(array(this.action_map[act], 0, idx), array(this.action_map[act], idx+1))
  })
  
  // Clear existing bindings for the current device from the target action
  var target_bindings = this.action_map[action_name]
  var i = length(target_bindings) - 1
  for (; i >= 0; i--) {
    if (detect_device(target_bindings[i]) == this.current_device)
      this.action_map[action_name] = array(array(this.action_map[action_name], 0, i), array(this.action_map[action_name], i+1))
  }
  
  // Only insert into the target if it's the right device
  if (detect_device(new_key) == this.current_device)  
    this.action_map[action_name].unshift(new_key)
}

// Returns bindings for the current device only
action.get_bindings_for_device = function(action_name) {
  var all = this.action_map[action_name] || []
  var self = this
  return filter(all, function(id) { return detect_device(id) == self.current_device })
}

// Returns the primary binding for display - prefer keyboard/mouse, then others
action.get_primary_binding = function(action_name) {
  var all = this.action_map[action_name] || []
  if (!length(all)) return '(unbound)'
  
  // Prefer keyboard/mouse bindings for display stability (mouse buttons now detect as keyboard)
  var keyboard = filter(all, function(id) { return detect_device(id) == 'keyboard' })
  if (length(keyboard)) return keyboard[0]
  
  // Fall back to any binding
  return all[0]
}

// Returns the binding for the current device, or "Unbound!" if none
action.get_current_device_binding = function(action_name) {
  var device_bindings = this.get_bindings_for_device(action_name)
  if (!length(device_bindings)) return 'Unbound!'
  return device_bindings[0]
}

action.save_bindings = function() {
  var self = this
  var _save = function() {
    io.slurpwrite('keybindings.json', json.encode(self.action_map))
  } disruption {
    log.console("Failed to save key bindings")
  }
  _save()
}

action.load_bindings = function() {
  var self = this
  var _load = function() {
    var data = null
    var bindings = null
    if (io.exists('keybindings.json')) {
      data = io.slurp('keybindings.json')
      bindings = object(json.decode(data), self.action_map)
    }
  } disruption {
    log.console("Failed to load key bindings")
  }
  _load()
}

action.reset_to_defaults = function() {
  this.action_map = object(default_action_map)
  this.save_bindings()
}

return function()
{
  var obj = meme(action)
  obj.action_map = object(default_action_map)
  obj.display_names = action_display_names
  obj.is_rebinding = false
  obj.rebind_target = null
  obj.current_device = 'keyboard'
  obj.current_gamepad_type = null
  obj.load_bindings()
  return obj
}
