#### libremiliacr
#### Copyright(C) 2020-2024 Remilia Scarlet <remilia@posteo.jp>
####
#### This program is free software: you can redistribute it and/or modify
#### it under the terms of the GNU General Public License as published
#### the Free Software Foundation, either version 3 of the License, or
#### (at your option) any later version.
####
#### This program is distributed in the hope that it will be useful,
#### but WITHOUT ANY WARRANTY; without even the implied warranty of
#### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
#### GNU General Public License for more details.
####
#### You should have received a copy of the GNU General Public License
#### along with this program.If not, see<http:####www.gnu.org/licenses/.>
require "./resolver"

module RemiLib::Config
  # The `ResolverSet` allows a collection of `Resolver` instances to be used in
  # a prioritized way.
  class ResolverSet
    @resolvers : Array(Resolver)

    # Creates a new `ResolverSet` instance.
    def initialize(*res : Resolver)
      @resolvers = res.to_a
    end

    # Returns the `Resolver` at the given index.
    def [](idx : Int)
      @resolvers[idx]
    end

    # Appends a `Resolver` to the end of this set.
    def <<(res : Resolver)
      @resolvers << res
    end

    # Inserts the given `Resolver` at the given index.
    def insert(idx : Int, res : Resolver)
      @resolvers.insert(idx, res)
    end

    # Deletes the given `Resolver` from the given index.
    def delete_at(idx : Int)
      @resolvers.delete_at(idx)
    end

    # If the given `Resolver` exists in this set, it is removed from this set
    # and returned.  Otherwise, this returns `nil`.
    def delete(res : Resolver) : Resolver?
      @resolvers.delete(res)
    end

    # Loops over all of the `Resolver` instances in this set, yielding each one
    # sequentially.
    def each(&)
      @resolvers.each do |res|
        yield res
      end
    end

    # Associates a data file named `filename` with a symbolic name for every one
    # of the associated `Resolver` instances.  The symbolic name must be unique
    # for every `Resolver`.
    def defineDataFile(symname : Symbol, filename : String|Path)
      @resolvers.each &.defineDataFile(symname, filename)
    end

    # Associates a data file named `filename` with a symbolic name for every one
    # of the associated `Resolver` instances.
    #
    # If the symbol is not unique to any of the `Resolver`s in this set, then
    # the old path is forgotten for that `Resolver` and the symbol becomes
    # associated with the new path.
    def defineDataFile!(symname : Symbol, filename : String|Path)
      @resolvers.each &.defineDataFile!(symname, filename)
    end

    protected def getDataFilePath(symname : Symbol) : Path?
      @resolvers.find do |res|
        if res.dataFiles.has_key?(symname)
          return res.getDataFilePath(symname)
        end
      end

      nil
    end

    # Given a symbolic name for a data file, this looks at each `Resolver` in
    # the order it appears in this set.  The first `Resolver` that knows about
    # the data file will return the `Path` associated with it.
    #
    # If the symbol is not registered with any `Resolver`s in this set, then
    # this raises an `UnknownDataFileError`.
    def dataFile(symname : Symbol) : Path
      getDataFilePath(symname) ||
        raise UnknownDataFileError.new("Data file is not registered in any Resolvers: #{symname}")
    end

    # Given a symbolic name for a data file, this looks at each `Resolver` in
    # the order it appears in this set.  The first `Resolver` that knows about
    # the data file will return the `Path` associated with it.
    #
    # If the symbol is not registered with any `Resolver`s in this set, then
    # this returns `nil`.
    def dataFile?(symname : Symbol) : Path?
      getDataFilePath(symname)
    end

    # Given a symbolic name for a data file, this looks at each `Resolver` in
    # the order it appears in the set.  The first `Resolver` that knows about
    # the data file will then open a new `File` stream and yield it.  If the
    # symbol is not registered with any `Resolver`, then this raises an
    # `UnknownDataFileError`.
    def dataFile(symname : Symbol, &) : Nil
      File.open(datafile(symname)) do |file|
        yield file
      end
    end

    # Given a symbolic name for a data file, this looks at each `Resolver` in
    # the order it appears in the set.  The first `Resolver` that knows about
    # the data file will then open a new `File` stream and yield it, then return
    # `true`.  If the symbol is not registered with any `Resolver`, then this
    # returns `false`.
    def dataFile?(symname : Symbol, &) : Bool
      if filename = dataFile?(symname)
        File.open(filename) do |file|
          yield file
        end
        true
      else
        false
      end
    end
  end
end
