module Dependencies
  class BaseDependency
    def fulfilled?()
      true
    end

    def name()
      self.class.name
    end

    def depends_on?(other)
      raise "#{self.class.name} has to implement #depends_on?"
    end
  end

  class Task < BaseDependency
    def initialize(instance)
      @instance = instance
    end

    def fulfilled?()
      if @instance.ran
        true
      else
        $logger.debug(" -> Dependency #{name} unfulfilled? (task #{@instance.inspect} hasn't run yet)")
        false
      end
    end

    def depends_on?(other)
      @instance.depends_on?(other)
    end
  end

  # When any of the dependencies given have been fulfilled, this dependency
  # will be fulfilled.
  class Any < BaseDependency
    def initialize(*dependencies)
      @dependencies = dependencies
    end

    def fulfilled?()
      @dependencies.any? { |dependency| dependency.fulfilled? }
    end

    def depends_on?(other)
      @dependencies.any? { |dependency| dependency.depends_on?(other) }
    end
  end

  class Files < BaseDependency
    def initialize(*patterns)
      @patterns = *patterns
    end

    def fulfilled?()
      if @patterns.all? { |pattern| Dir.glob(pattern).count > 0 }
        true
      else
        $logger.debug do
          patterns = @patterns.reject do |pattern|
            Dir.glob(pattern).count > 0
          end.join(", ")

          " -> Dependency #{name} unfulfilled? (Pattern #{patterns} does not match paths)"
        end
        false
      end
    end

    # It is unknown what creates the file.
    # For mount points, prefer the +Mount+ dependency type.
    def depends_on?(other)
      false
    end
  end

  # Checks in sysfs for the given network interface names.
  class NetworkInterface < Files
    def initialize(*names)
      super(*names.map { |name| File.join("/sys/class/net", name) })
    end
  end

  class Target < BaseDependency
    def initialize(name)
      @name = name.to_sym
    end

    def fulfilled?()
      task.ran
    end

    def depends_on?(other)
      task.depends_on?(other)
    end

    def task()
      Targets[@name]
    end
  end
end