module Vfs
  class Dir < Entry
    #
    # Container
    #
    def [] path
      path = path.to_s
      if path =~ /.+[\/]$/
        path = path.sub /\/$/, ''
        dir path
      else
        entry path
      end
    end
    alias_method :/, :[]


    #
    # Attributes
    #
    alias_method :exist?, :dir?


    #
    # CRUD
    #
    def create options = {}
      driver.open do
        try = 0
        begin
          try += 1
          driver.create_dir path
        rescue StandardError => error
          entry = self.entry
          attrs = entry.get
          if attrs and attrs[:file] #entry.exist?
            entry.destroy
          elsif attrs and attrs[:dir]
            # dir already exist, no need to recreate it
            return self
          else
            parent = self.parent
            if parent.exist?
              # some unknown error
              raise error
            else
              parent.create(options)
            end
          end

          try < 2 ? retry : raise(error)
        end
      end
      self
    end

    def destroy options = {}
      destroy_entry :dir, :file
    end


    #
    # Content
    #
    def entries *args, &block
      raise "invalid arguments #{args.inspect}!" if args.size > 2
      options = args.last.is_a?(Hash) ? args.pop : {}
      query = args.first
      options[:bang] = true unless options.include? :bang
      filter = options[:filter]
      type_required = options[:type]

      driver.open do
        begin
          list = []
          # query option is optional and supported only for some drivers (local driver for example)
          driver.each_entry path, query do |name, type|
            # for performance reasons some drivers may return the type of entry as
            # optionally evaluated callback.
            type = type.call if (filter or type_required) and type.is_a?(Proc)

            next if filter and (filter != type)

            entry = if type == :dir
              dir(name)
            elsif type == :file
              file(name)
            else
              entry(name)
            end
            block ? block.call(entry) : (list << entry)
          end
          block ? nil : list
        rescue StandardError => error
          attrs = get
          if attrs and attrs[:file]
            raise Error, "can't query entries on File ('#{self}')!"
          elsif attrs and attrs[:dir]
            # some unknown error
            raise error
          else
            # TODO2 remove :bang
            raise Error, "'#{self}' not exist!" if options[:bang]
            []
          end
        end
      end
    end
    alias_method :each, :entries

    def files *args, &block
      options = args.last.is_a?(Hash) ? args.pop : {}

      options[:filter] = :file
      args << options
      entries *args, &block
    end

    def dirs *args, &block
      options = args.last.is_a?(Hash) ? args.pop : {}

      options[:filter] = :dir
      args << options
      entries *args, &block
    end

    def include? name
      entry[name].exist?
    end
    alias_method :has?, :include?

    def empty?
      catch :break do
        entries{|e| throw :break, false}
        true
      end
    end


    #
    # Transfers
    #
    def copy_to to, options = {}
      options[:bang] = true unless options.include? :bang

      raise Error, "invalid argument, destination should be a Entry (#{to})!" unless to.is_a? Entry
      raise Error, "you can't copy to itself" if self == to

      target = if to.is_a? File
        to.dir
      elsif to.is_a? Dir
        to.dir #(name)
      elsif to.is_a? UniversalEntry
        # raise "can't copy Dir to File ('#{self}')!" if to.file? and !options[:override]
        to.dir #.create
      else
        raise "can't copy to unknown Entry!"
      end

      # efficient_dir_copy(target, options) || unefficient_dir_copy(target, options)
      unefficient_dir_copy(target, options)

      target
    end

    def move_to to, options = {}
      copy_to to, options
      destroy options
      to
    end

    # class << self
    #   attr_accessor :dont_use_efficient_dir_copy
    # end

    protected
      def unefficient_dir_copy to, options
        to.create options
        entries options.merge(type: true) do |e|
          if e.is_a? Dir
            e.copy_to to.dir(e.name), options
          elsif e.is_a? File
            e.copy_to to.file(e.name), options
          else
            raise 'internal error'
          end
        end
      end


      # def efficient_dir_copy to, options
      #   return false if self.class.dont_use_efficient_dir_copy
      #
      #   driver.open do
      #     try = 0
      #     begin
      #       try += 1
      #       self.class.efficient_dir_copy(self, to, options[:override])
      #     rescue StandardError => error
      #       unknown_errors = 0
      #
      #       attrs = get
      #       if attrs and attrs[:file]
      #         raise Error, "can't copy File as a Dir ('#{self}')!"
      #       elsif attrs and attrs[:dir]
      #         # some unknown error (but it also maybe caused by to be fixed error in 'to')
      #         unknown_errors += 1
      #       else
      #         raise Error, "'#{self}' not exist!" if options[:bang]
      #         return true
      #       end
      #
      #       attrs = to.get
      #       if attrs and attrs[:file]
      #         if options[:override]
      #           to.destroy
      #         else
      #           raise Vfs::Error, "entry #{to} already exist!"
      #         end
      #       elsif attrs and attrs[:dir]
      #         unknown_errors += 1
      #         # if options[:override]
      #         #   to.destroy
      #         # else
      #         #   dir_already_exist = true
      #         #   # raise Vfs::Error, "entry #{to} already exist!"
      #         # end
      #       else # parent not exist
      #         parent = to.parent
      #         if parent.exist?
      #           # some unknown error (but it also maybe caused by already fixed error in 'from')
      #           unknown_errors += 1
      #         else
      #           parent.create(options)
      #         end
      #       end
      #
      #       raise error if unknown_errors > 1
      #       try < 2 ? retry : raise(error)
      #     end
      #   end
      # end
      #
      # def self.efficient_dir_copy from, to, override
      #   from.driver.open{
      #     driver.respond_to?(:efficient_dir_copy) and driver.efficient_dir_copy(from, to, override)
      #   } or
      #   to.driver.open{
      #     driver.respond_to?(:efficient_dir_copy) and driver.efficient_dir_copy(from, to, override)
      #   }
      # end
  end
end