module Watir
  module Generator
    class Base::SpecExtractor

      IDL_SELECTOR = "//pre[contains(@class, 'idl')]".freeze

      class InterfaceNotFound < StandardError; end

      def initialize(uri)
        @uri = uri
      end

      def process
        download_and_parse
        extract_idl_parts
        extract_interface_map
        drop_issued_interfaces
        build_result
      rescue
        p errors
        raise
      end

      def errors
        @errors ||= []
      end

      #
      # returns a topoligically sorted array of WebIDL::Ast::Interface objects
      #

      def sorted_interfaces
        process if @interfaces.nil?

        sorter.sort.map { |name|
          @interfaces.find { |i| i.name == name } or puts "ignoring interface: #{name}"
        }.compact
      end

      def print_hierarchy
        process if @interfaces.nil?
        sorter.print
      end

      def fetch_interface(interface)
        @interfaces_by_name[interface] or raise InterfaceNotFound, "#{interface} not found in IDL"
      end

      private

      def download_and_parse
        open(@uri) { |io| @doc = Nokogiri.HTML(io) }
      end

      def extract_idl_parts
        parsed = @doc.search(IDL_SELECTOR).map {  |e| parse_idl(e.inner_text) }.compact

        implements = []
        @interfaces = []

        parsed.flatten.each do |element|
          case element
          when WebIDL::Ast::Interface
            @interfaces << element
          when WebIDL::Ast::ImplementsStatement
            implements << element
          end
        end

        @interfaces_by_name = @interfaces.group_by(&:name)
        apply_implements(implements)
        merge_interfaces
      end

      def extract_interface_map
        raise NotImplementedError
      end

      def drop_issued_interfaces
        @interface_map.delete_if do |_, interface|
          issued_interfaces.include?(interface)
        end
      end

      def build_result
        raise NotImplementedError
      end

      def parse_idl(str)
        result = idl_parser.parse(str)

        if result
          result.build
        else
          errors << idl_parser.failure_reason
          nil
        end
      end

      def apply_implements(implements)
        implements.each do |is|
          implementor_name = is.implementor.gsub(/^::/, '')
          implementee_name = is.implementee.gsub(/^::/, '')

          begin
            intf = fetch_interface(implementor_name).first
            intf.implements << fetch_interface(implementee_name).first
          rescue InterfaceNotFound => ex
            puts ex.message
          end
        end
      end

      def merge_interfaces
        non_duplicates = @interfaces.uniq(&:name)
        duplicates = @interfaces - non_duplicates

        duplicates.each do |intf|
          final = non_duplicates.find { |i| i.name == intf.name }
          final.inherits += intf.inherits
          final.members += intf.members
          final.extended_attributes += intf.extended_attributes
        end

        @interfaces = non_duplicates
      end

      def idl_parser
        @idl_parser ||= WebIDL::Parser::IDLParser.new
      end

      def sorter
        @idl_sorter ||= Base::IDLSorter.new(@interfaces)
      end

    end # SpecExtractor
  end # Generator
end # Watir