# -*- encoding: utf-8 -*-

require 'webgen/extension_manager'
require 'webgen/error'

module Webgen

  # Namespace for all classes that are used to write content to a specific destination.
  #
  # == About
  #
  # This class is used to manage destination classes. A destination class is a class that writes
  # content to a specific destination. For example, the FileSystem class uses the file system to
  # write out the generated content to the file system.
  #
  # The #register method is used for registering new destination classes.
  #
  # Since there can only be one destination class per website instance, this manager class hides the
  # implementation details and uses the configuration option "destination" to create a new object
  # from the correct destination class. Use the #write, #read, #exists? and #delete methods on this
  # manager class to perform the equivalent operations on the destination class.
  #
  # == Implementing a destination class
  #
  # A destination class must respond to the following five methods:
  #
  # [initialize(website, *args)]
  #   The website instance is always provided as the first argument and the initialize method can
  #   have any number of other parameters.
  #
  # [exists?(path)]
  #   Return +true+ if the given path exists.
  #
  # [delete(path)]
  #   Delete the given path.
  #
  # [write(path, data)]
  #   Write the +data+ to the given +path+. If +path+ ends with a slash, a directory should be
  #   created. The parameter +data+ is either a String with the content or a Webgen::Path object. If
  #   it is the latter, use the Webgen::Path#io method for retrieving the IO object.
  #
  # [read(path, mode = 'rb')]
  #   Return the content of the given path if it exists or raise an error otherwise. The parameter
  #   +mode+ specifies the mode in which the path should be opened and defaults to reading in binary
  #   mode.
  #
  #   It seems a bit odd that a destination object has to implement reading functionality. However,
  #   consider the case where you want webgen to render a website programmatically and *use* the
  #   generated data. In this case you need a way to get the content of the generated files!
  #
  # == Sample destination class
  #
  # Following is a simple destination class which stores the written content in a hash in memory:
  #
  #   class MemoryDestination
  #
  #     attr_reader :data
  #
  #     def initialize(website)
  #       # the website object is not used in this destination class
  #       @data = {}
  #     end
  #
  #     def exists?(path)
  #       @data.has_key?(path)
  #     end
  #
  #     def delete(path)
  #       @data.delete(path)
  #     end
  #
  #     def write(path, data)
  #       @data[path] = (data.kind_of?(String) ? data : data.data)
  #     end
  #
  #     def read(path, mode = 'rb')
  #       raise "No such path #{path}" unless @data[path] && path[-1] != ?/
  #       @data[path]
  #     end
  #   end
  #
  #   website.ext.destination.register MemoryDestination, :name => 'memory'
  #
  class Destination

    include Webgen::ExtensionManager

    # Create a new Destination object that is associated with the given website.
    def initialize(website)
      super()
      @website = website
    end

    # Register a destination class.
    #
    # The parameter +klass+ has to contain the name of the destination class or the class object
    # itself. If the class is located under this namespace, only the class name without the
    # hierarchy part is needed, otherwise the full class name including parent module/class names is
    # needed.
    #
    # === Options:
    #
    # [:name] The name for the destination class. If not set, it defaults to the snake-case version
    #         (i.e. FileSystem → file_system) of the class name (without the hierarchy part). It
    #         should only contain letters.
    #
    # === Examples:
    #
    #   destination.register('FileSystem')   # registers Webgen::Destination::FileSystem
    #
    #   destination.register('::FileSystem') # registers FileSystem !!!
    #
    #   destination.register('MyModule::Doit', name: 'doit_now')
    #
    def register(klass, options={}, &block)
      do_register(klass, options, false, &block)
    end

    # Return the instance of the configured destination class.
    def instance
      if !defined?(@instance)
        name, *args = @website.config['destination']
        @instance = extension(name).new(@website, *args)
      end
      @instance
    end
    private :instance

    # Return +true+ if the given path exists.
    def exists?(path)
      instance.exists?(path)
    end

    # Delete the given +path+
    def delete(path)
      instance.delete(path)
    end

    # Write the +data+ (either a String or a Webgen::Path object) to the given +path+.
    def write(path, data)
      instance.write(path, data) if !@website.config['website.dry_run']
    end

    # Return the content of the given +path+ which is opened in +mode+.
    def read(path, mode = 'rb')
      instance.read(path, mode)
    end

  end

end
