require 'sablon/document_object_model/file_handler'
require 'sablon/document_object_model/content_types'
require 'sablon/document_object_model/numbering'
require 'sablon/document_object_model/relationships'

module Sablon
  # Stores classes used to build and interact with the template by treating
  # it as a full document model instead of disparate components that are
  # packaged together.
  module DOM
    class << self
      # Allows new handlers to be registered for different components of
      # the MS Word document. The pattern passed in is used to determine
      # if a file in the entry set should be handled by the class.
      def register_dom_handler(pattern, klass)
        handlers[pattern] = klass
        klass.extend_model(Sablon::DOM::Model)
      end

      def wrap_with_handler(entry_name, content)
        key = handlers.keys.detect { |pat| entry_name =~ pat }
        if key
          handlers[key].new(content)
        else
          Sablon::DOM::FileHandler.new(content)
        end
      end

      private

      def handlers
        @handlers ||= {}
      end
    end

    # Object to represent an entire template and it's XML contents
    class Model
      attr_accessor :current_entry
      attr_reader :zip_contents

      # setup the DOM by reading and storing all XML files in the template
      # in memory
      def initialize(zip_io_stream)
        @current_entry = nil
        @zip_contents = {}
        zip_io_stream.each do |entry|
          next unless entry.file?
          content = entry.get_input_stream.read
          @zip_contents[entry.name] = wrap_entry(entry.name, content)
        end
        #
        @dom = build_dom(@zip_contents)
      end

      # Returns the corresponding DOM handled file
      def [](entry_name)
        @dom[entry_name]
      end

      private

      # Determines how the content in the zip file entry should be wrapped
      def wrap_entry(entry_name, content)
        if entry_name =~ /\.(?:xml|rels)$/
          Nokogiri::XML(content)
        else
          content
        end
      end

      # constructs the dom model using helper clases defined under this
      # namespace.
      def build_dom(entries)
        key_values = entries.map do |entry_name, content|
          [entry_name, Sablon::DOM.wrap_with_handler(entry_name, content)]
        end
        #
        Hash[key_values]
      end

      def create_entry_if_not_exist(name, init_content = '')
        return unless @zip_contents[name].nil?
        #
        # create the entry and add it to the dom
        @zip_contents[name] = wrap_entry(name, init_content)
        @dom[name] = Sablon::DOM.wrap_with_handler(name, @zip_contents[name])
      end
    end

    register_dom_handler(%r{word/numbering.xml}, Sablon::DOM::Numbering)
    register_dom_handler(/.rels$/, Sablon::DOM::Relationships)
    register_dom_handler(/Content_Types/, Sablon::DOM::ContentTypes)
  end
end