# frozen_string_literal: true

module Watir
  module Scrolling
    def scroll
      Scroll.new(self)
    end
  end

  class Scroll
    def initialize(object)
      @driver = object.browser.wd
      @object = object
      @origin = nil
    end

    # Scrolls by offset.
    # @param [Fixnum] left Horizontal offset
    # @param [Fixnum] top Vertical offset
    #
    def by(left, top)
      if @origin
        @driver.action.scroll_from(@origin, left, top).perform
      else
        @object.browser.execute_script('window.scrollBy(arguments[0], arguments[1]);', Integer(left), Integer(top))
      end
      self
    end

    # Scrolls to specified location.
    # @param [Symbol] param
    #
    def to(param = :top)
      return scroll_to_element if param == :viewport

      args = @object.is_a?(Watir::Element) ? element_scroll(param) : browser_scroll(param)
      raise ArgumentError, "Don't know how to scroll #{@object} to: #{param}!" if args.nil?

      @object.browser.execute_script(*args)
      self
    end

    # Sets an origin point for a future scroll
    # @param [Integer] x_offset
    # @param [Integer] y_offset
    def from(x_offset = 0, y_offset = 0)
      @origin = if @object.is_a?(Watir::Element)
                  Selenium::WebDriver::WheelActions::ScrollOrigin.element(@object.we, x_offset, y_offset)
                else
                  Selenium::WebDriver::WheelActions::ScrollOrigin.viewport(x_offset, y_offset)
                end
      self
    end

    private

    def scroll_to_element
      @driver.action.scroll_to(@object.we).perform
    end

    def element_scroll(param)
      script = case param
               when :top, :start
                 'arguments[0].scrollIntoView();'
               when :center
                 <<-JS
                   var bodyRect = document.body.getBoundingClientRect();
                   var elementRect = arguments[0].getBoundingClientRect();
                   var left = (elementRect.left - bodyRect.left) - (window.innerWidth / 2);
                   var top = (elementRect.top - bodyRect.top) - (window.innerHeight / 2);
                   window.scrollTo(left, top);
                 JS
               when :bottom, :end
                 'arguments[0].scrollIntoView(false);'
               else
                 return nil
               end
      [script, @object]
    end

    def browser_scroll(param)
      case param
      when :top, :start
        'window.scrollTo(0, 0);'
      when :center
        y = '(document.body.scrollHeight - window.innerHeight) / 2 + document.body.getBoundingClientRect().top'
        "window.scrollTo(window.outerWidth / 2, #{y});"
      when :bottom, :end
        'window.scrollTo(0, document.body.scrollHeight);'
      when Array
        ['window.scrollTo(arguments[0], arguments[1]);', Integer(param[0]), Integer(param[1])]
      end
    end
  end
end