require 'set'

module Redcar
  class Project

    class FindFileDialog < FilterListDialog
      attr_reader :project

      def initialize(project)
        super()
        @project = project
      end

      def close
        super
      end

      def paths_for(filter)
        paths = recent_files if filter.length < 2
        paths ||= find_files_from_list(filter, recent_files) + find_files(filter, project.path)
        paths.uniq
      end

      def update_list(filter)
        full_paths = paths_for(filter)
        display_paths = full_paths.map { |path| display_path(path) }
        if display_paths.uniq.length < full_paths.length
          display_paths = expand_duplicates(display_paths, full_paths)
        end
        result = []
        display_paths.zip(full_paths) do |display_path, full_path|
          icon = File.basename(full_path).split(".").last.split(//).first.downcase
          if icon =~ /[a-z]/
            icon = icon == "a" ? "document-attribute" : "document-attribute-#{icon}"
          else
            icon = "document"
          end
          result << {:name => display_path, :path => full_path, :icon => :"#{icon}"}
        end
        result
      end

      def selected(item, ix)
        if item[:path] and File.exist?(item[:path])
          close
          if win = Redcar.app.focussed_window and tab = win.focussed_notebook_tab and doc = tab.document
            Redcar.app.navigation_history.save(doc)
          end
          OpenFileCommand.new(item[:path]).run
          cur_doc = Redcar.app.focussed_window.focussed_notebook_tab.document
          Redcar.app.navigation_history.save(cur_doc) if cur_doc
        end
      end

      private

      def recent_files
        files = project.recent_files
        ((files[0..-2]||[]).reverse + [files[-1]]).compact
      end

      # Find duplicates by checking if index from left and right equal
      def duplicates(enum)
        Set[*enum.select {|k| enum.index(k) != enum.rindex(k) }]
      end

      # search out and expand duplicates in shortened paths to their full length
      def expand_duplicates(display_paths, full_paths)
        duplicates = duplicates(display_paths)
        display_paths.each_with_index do |dp, i|
          if duplicates.include? dp
            display_paths[i] = display_path(full_paths[i], project.path.split('/')[0..-2].join('/'))
          end
        end
      end

      def display_path(path, first_remove_this_prefix = nil)
        n = -3
        if first_remove_this_prefix and path.index(first_remove_this_prefix) == 0
          path = path[first_remove_this_prefix.length..-1]
          # show the full subdirs in the case of collisions
          n = -100
        end

        if path.count('/') > 0
          count_back = [-path.count('/'), n].max
          path.split("/").last +
            " (" +
            path.split("/")[count_back..-2].join("/") +
            ")"
        else
          path
        end
      end

      def find_files_from_list(text, file_list)
        re = make_regex(text)
        file_list.select do |fn|
          match_substring(fn) =~ re
        end
      end

      def find_files(text, directories)
        begin
          files = project.all_files.sort
        rescue => e
          p e
        end
        filter_and_rank_by(files, text) {|fn| match_substring(fn) }
      end

      def match_substring(filename)
        @folder_search ? filename.sub(project.path, "") : filename.split("/").last
      end

      def make_regex(text)
        path = text.gsub(/\s/, "").split("/")
        @folder_search = path.size > 1
        folders = path[0...-1].map {|f| "(#{Regexp.escape(f)}).*/" }
        file = path.last.each_char.map {|c| "(#{Regexp.escape(c)})[^/]*?" }.join
        re_src = "#{folders.join}#{file}$"
        Regexp.new(re_src, :options => Regexp::IGNORECASE)
      end
    end
  end
end
