require 'stache/handlebars/view'

module Stache
  module Handlebars
    # From HAML, thanks a bunch, guys!
    # In Rails 3.1+, template handlers don't inherit from anything. In <= 3.0, they do.
    # To avoid messy logic figuring this out, we just inherit from whatever the ERB handler does.
    class Handler < Stache::Util.av_template_class(:Handlers)::ERB.superclass
      if Stache::Util.needs_compilable?
        include Stache::Util.av_template_class(:Handlers)::Compilable
      end

      def compile(template)
        handlebars_class = handlebars_class_from_template(template)
        <<-RUBY_CODE
          handlebars = ::#{handlebars_class}.new
          handlebars.view = self

          handlebars.register_helper('helperMissing') do |name, *args|
            meth, *params, options = args

            if handlebars.respond_to?(meth)
              handlebars.send(meth, *params)
            elsif self.respond_to?(meth)
              self.send(meth, *params)
            elsif params.size == 0
              ""
            else
              raise "Could not find property '\#\{meth\}'"
            end
          end

          handlebars.register_helper('yield') do |name, *args|
            content_for(:layout)
          end

          template = handlebars.compile('#{template.source.gsub(/'/, "\\\\'")}')
          vars = {}
          partial_renderer = PartialRenderer.new(lookup_context)
          vars.merge!(@_assigns)
          vars.merge!(local_assigns || {})
          options = partial_renderer.instance_variable_get('@options')
          vars.merge!(options[:context] || {}) if options

          handlebars.partial_missing do |name|
            search_path = '#{template.virtual_path}'.split("/")[0..-2]
            file = (search_path + [name]).join("/")
            finder = lambda do |partial|
              self.lookup_context.find(file, [], partial, [], {formats: [:html]})
            end
            template = finder.call(false) rescue finder.call(true)

            if template
              t = handlebars.compile(template.source)
              t.call(vars).html_safe
            else
              raise "Could not find template '\#\{file\}'"
            end
          end

          template.call(vars).html_safe
        RUBY_CODE
      end

      # In Rails 3.1+, #call takes the place of #compile
      def self.call(template)
        new.compile(template)
      end

      # suss out a constant name for the given template
      def handlebars_class_from_template(template)
        const_name = ActiveSupport::Inflector.camelize(template.virtual_path.to_s)
        const_name = "#{Stache.wrapper_module_name}::#{const_name}" if Stache.wrapper_module_name

        begin
          const_name.constantize
        rescue NameError, LoadError => e
          # Only rescue NameError/LoadError concerning our mustache_class
          if e.message.match(/#{const_name}$/)
            Stache::Handlebars::View
          else
            raise e
          end
        end
      end

    end
  end
end