module Capybara::Cuprite::Browser::Input

def calculate_quads(node, offset_x = nil, offset_y = nil)

def calculate_quads(node, offset_x = nil, offset_y = nil)
  quads = get_content_quads(node)
  offset_x, offset_y = offset_x.to_i, offset_y.to_i
  if offset_x > 0 || offset_y > 0
    point = quads.first
    [point[:x] + offset_x, point[:y] + offset_y]
  else
    x, y = quads.inject([0, 0]) do |memo, point|
      [memo[0] + point[:x],
       memo[1] + point[:y]]
    end
    [x / 4, y / 4]
  end
end

def click(node, keys = [], offset = {})

def click(node, keys = [], offset = {})
  x, y, modifiers = prepare_before_click(__method__, node, keys, offset)
  command("Input.dispatchMouseEvent", type: "mousePressed", modifiers: modifiers, button: "left", x: x, y: y, clickCount: 1)
  @wait = 0.05 # Potential wait because if network event is triggered then we have to wait until it's over.
  command("Input.dispatchMouseEvent", type: "mouseReleased", modifiers: modifiers, button: "left", x: x, y: y, clickCount: 1)
end

def click_coordinates(x, y)

def click_coordinates(x, y)
  command("Input.dispatchMouseEvent", type: "mousePressed", button: "left", x: x, y: y, clickCount: 1)
  @wait = 0.05 # Potential wait because if network event is triggered then we have to wait until it's over.
  command("Input.dispatchMouseEvent", type: "mouseReleased", button: "left", x: x, y: y, clickCount: 1)
end

def double_click(node, keys = [], offset = {})

def double_click(node, keys = [], offset = {})
  x, y, modifiers = prepare_before_click(__method__, node, keys, offset)
  command("Input.dispatchMouseEvent", type: "mousePressed", modifiers: modifiers, button: "left", x: x, y: y, clickCount: 2)
  command("Input.dispatchMouseEvent", type: "mouseReleased", modifiers: modifiers, button: "left", x: x, y: y, clickCount: 2)
end

def drag(node, other)

def drag(node, other)
  raise NotImplementedError
end

def drag_by(node, x, y)

def drag_by(node, x, y)
  raise NotImplementedError
end

def get_content_quads(node)

def get_content_quads(node)
  result = command("DOM.getContentQuads", nodeId: node["nodeId"])
  raise "Node is either not visible or not an HTMLElement" if result["quads"].size == 0
  # FIXME: Case when a few quads returned
  result["quads"].map do |quad|
    [{x: quad[0], y: quad[1]},
     {x: quad[2], y: quad[3]},
     {x: quad[4], y: quad[5]},
     {x: quad[6], y: quad[7]}]
  end.first
end

def hover(node)

def hover(node)
  evaluate_on(node: node, expr: "_cuprite.scrollIntoViewport(this)")
  x, y = calculate_quads(node)
  command("Input.dispatchMouseEvent", type: "mouseMoved", x: x, y: y)
end

def prepare_before_click(name, node, keys, offset)

def prepare_before_click(name, node, keys, offset)
  evaluate_on(node: node, expr: "_cuprite.scrollIntoViewport(this)")
  x, y = calculate_quads(node, offset[:x], offset[:y])
  evaluate_on(node: node, expr: "_cuprite.mouseEventTest(this, '#{name}', #{x}, #{y})")
  click_modifiers = { alt: 1, ctrl: 2, control: 2, meta: 4, command: 4, shift: 8 }
  modifiers = keys.map { |k| click_modifiers[k.to_sym] }.compact.reduce(0, :|)
  command("Input.dispatchMouseEvent", type: "mouseMoved", x: x, y: y)
  [x, y, modifiers]
end

def right_click(node, keys = [], offset = {})

def right_click(node, keys = [], offset = {})
  x, y, modifiers = prepare_before_click(__method__, node, keys, offset)
  command("Input.dispatchMouseEvent", type: "mousePressed", modifiers: modifiers, button: "right", x: x, y: y, clickCount: 1)
  command("Input.dispatchMouseEvent", type: "mouseReleased", modifiers: modifiers, button: "right", x: x, y: y, clickCount: 1)
end

def scroll_to(left, top)

def scroll_to(left, top)
  raise NotImplementedError
end

def select(node, value)

def select(node, value)
  evaluate_on(node: node, expr: "_cuprite.select(this, #{value})")
end

def send_keys(node, keys)

def send_keys(node, keys)
  # value.each_char do |char|
  #   # Check puppeteer Input.js and USKeyboardLayout.js
  #   # also send_keys and modifiers from capybara API and unify all that.
  #   if /\n/.match?(char)
  #     command("Input.insertText", text: char)
  #     # command("Input.dispatchKeyEvent", type: "keyDown", code: "Enter", key: "Enter", text: "\r")
  #     # command("Input.dispatchKeyEvent", type: "keyUp", code: "Enter", key: "Enter")
  #   else
  #     command("Input.dispatchKeyEvent", type: "keyDown", text: char)
  #     command("Input.dispatchKeyEvent", type: "keyUp", text: char)
  #   end
  # end
  # command "send_keys", node, normalize_keys(keys)
  raise NotImplementedError
end

def set(node, value)

def set(node, value)
  object_id = command("DOM.resolveNode", nodeId: node["nodeId"]).dig("object", "objectId")
  evaluate("_cuprite.set(arguments[0], arguments[1])", { "objectId" => object_id }, value)
end

def trigger(node, event)

def trigger(node, event)
  options = event.to_s == "click" ? { wait: 0.1 } : {}
  evaluate_on(node: node, expr: %(_cuprite.trigger(this, "#{event}")), **options)
end