require 'kookaburra/ui_driver/mixins/has_browser'
require 'kookaburra/ui_driver/mixins/has_strategies'
require 'kookaburra/ui_driver/mixins/has_subcomponents'

module Kookaburra
  class UIDriver
    class UIComponent
      include Kookaburra::Assertion
      include HasBrowser
      include HasStrategies
      extend HasSubcomponents

      ##### Class macros #####
      def self.component_locator(locator)
        define_method(:component_locator) { locator }
      end

      def self.component_path(path)
        case path
        when Symbol
          alias_method :component_path, path
        else
          define_method(:component_path) { path }
        end
      end

      def self.path_id_regex(regex)
        define_method(:path_id_regex) { regex }
      end

      ##### Instance methods #####

      # Returns the CSS locator used to locate the component in the DOM
      def component_locator
        raise "You must define component_locator in subclasses of UIComponent!"
      end

      def visible!
        raise "#{self.class} not currently visible!" unless visible?
      end

      def visible?
        v= component_visible?
        no_500_error! unless v
        v
      end

      # Default implementation navigates directly to this UIComponent's
      # #component_path. If +opts[:query_params] is set to a Hash, the request
      # will be made with the resulting querystring on the URL.
      def show(opts = {})
        unless respond_to?(:component_path)
          raise "You must either set component_path or redefine the #show method in UIComponent subclasses!"
        end
        return if visible?
        path = component_path
        path << ( '?' + opts[:query_params].map{|kv| "%s=%s" % kv}.join('&') ) if opts[:query_params]
        visit path
      end

      def refresh
        visit component_path
      end

      def show!(opts = {})
        show opts
        visible!
      end

      def at_path?
        (Array(component_path) + Array(alternate_paths)).include?(browser.current_path)
      end

      def component_visible?
        return false if respond_to?(:component_path) && !at_path?
        browser.has_css?(component_locator)
      end

      def alternate_paths
        []
      end

    private
      def id_from_path
        browser.current_path =~ path_id_regex
        $1.present? ? $1.to_i : nil
      end

      def fill_in(locator, options)
        in_component { browser.fill_in(locator, options) }
      end

      def click_on(locator)
        in_component { browser.find(locator).click }
      end

      def choose(locator)
        in_component { browser.choose(locator) }
      end

      def in_component(&blk)
        browser.within(component_locator, &blk)
      end
    end
  end
end