require 'find'

module Ore
  module Template
    #
    # Represents a template directory and the static files, ERb files
    # and sub-directories within it.
    #
    class Directory

      # Files or directory names to ignore
      @@ignore = %w[.git]

      # The known markup languages and file extensions
      @@markups = {
        :markdown => %w[.md .markdown],
        :textile => %w[.tt .textile],
        :rdoc => %w[.rdoc]
      }

      # The path of the template directory
      attr_reader :path

      # The directories within the template directory
      attr_reader :directories

      # The static files in the template directory
      attr_reader :files

      # The ERb templates in the template directory
      attr_reader :templates

      # The include templates in the template directory
      attr_reader :includes

      #
      # Initializes a new template directory.
      #
      # @param [String] path
      #   The path to the template directory.
      #
      def initialize(path)
        @path = File.expand_path(path)

        @directories = []
        @files = {}
        @templates = {}
        @includes = Hash.new { |hash,key| hash[key] = {} }

        scan!
      end

      #
      # Enumerates through the directories in the template directory.
      #
      # @yield [path]
      #   The given block will be passed each directory path.
      #
      # @yieldparam [String] path
      #   The relative path of a directory within the template directory.
      #
      def each_directory(&block)
        @directories.each(&block)
      end

      #
      # Enumerates through every file in the template directory.
      #
      # @param [Symbol] markup
      #   The markup to look for.
      #
      # @yield [path]
      #   The given block will be passed each file path.
      #
      # @yieldparam [String] path
      #   A relative path of a file within the template directory.
      #
      def each_file(markup)
        @files.each do |dest,file|
          if (formatted_like?(dest,markup) || !formatted?(dest))
            yield dest, file
          end
        end
      end

      #
      # Enumerates over every template within the template directory.
      #
      # @param [Symbol] markup
      #   The markup to look for.
      #
      # @yield [path]
      #   The given block will be passed each template path.
      #
      # @yieldparam [String] path
      #   A relative path of a template within the template directory.
      #
      def each_template(markup)
        @templates.each do |dest,file|
          if (formatted_like?(dest,markup) || !formatted?(dest))
            yield dest, file
          end
        end
      end

      protected

      #
      # Scans the template directory recursively recording the directories,
      # files and partial templates.
      #
      def scan!
        Dir.chdir(@path) do
          Find.find('.') do |file|
            next if file == '.'

            # ignore the ./
            file = file[2..-1]
            name = File.basename(file)

            # ignore certain files/directories
            Find.prune if @@ignore.include?(name)

            if File.directory?(file)
              @directories << file
            elsif File.file?(file)
              src = File.join(@path,file)

              case File.extname(name)
              when '.erb'
                # erb template
                if name[0,1] == '_'
                  # partial template
                  template_dir = File.dirname(file)
                  template_name = name[1...-4].to_sym

                  @includes[template_dir][template_name] = src
                else
                  dest = file[0...-4]

                  @templates[dest] = src
                end
              else
                # static file
                @files[file] = src
              end
            end
          end
        end
      end

      #
      # Determines whether a file is markup formatted.
      #
      # @param [String] path
      #   The path to the file.
      #
      # @return [Boolean]
      #   Specifies whether the file is formatting.
      #
      def formatted?(path)
        @@markups.values.any? { |exts| exts.include?(File.extname(path)) }
      end

      #
      # Determines if a file has a specific type of markup formatting.
      #
      # @param [String] path
      #   The path to the file.
      #
      # @param [Symbol] markup
      #   The specified type of markup.
      #
      # @return [Boolean]
      #   Specifies whether the file has the sepcified formatting.
      #
      def formatted_like?(path,markup)
        @@markups[markup].include?(File.extname(path))
      end

    end
  end
end