require 'amrita/template'

module Amrita
  class ExpandContext
    attr_accessor :parts
  end

  module PartsTemplate
    include Amrita

    class PartsTempleteExpander
      include Amrita::DictionaryData

      def initialize(pt)
        @pt = pt
      end

      def amrita_get_data(key, e, context) #:nodoc:
        m = @pt.method(key)
        if m 
          case m.arity
          when 0
            @pt.__send__(key)
          when 1
            @pt.__send__(key, e)
          else
            @pt.__send__(key, e, context)          
          end
        else
          @pt.__send__(key)
        end
      end
    end

    def ptx
      unless defined? @ptx
        @ptx = PartsTempleteExpander.new(self)
      end
      @ptx
    end

    def amrita_expand_element(e, context=DefaultContext.clone)
      e.clone { parts_template.expand(ptx, context) }
    end

    def to_s
      SanitizedString.new(parts_template.expand(ptx).to_s)
    end
  end

  class Template
    def install_parts_to(mod)
      setup_template if need_update?
      template.each_element do |e|
        cls_name = e[:class]
        next unless cls_name
        next unless cls_name =~ /[A-Z]\w*/
        
        e[:class] = nil
        install_parts_template(mod, e, cls_name) 
      end
    end

    def install_parts_template(mod, ee, cls_name)
      e = ee.clone
      e[:id] = nil
      parts_mod = nil
      if mod.const_defined?(cls_name)
        parts_mod = mod.const_get(cls_name)
        raise "#{cls_name} is not a Module but [#{parts_mod.type}]" unless parts_mod.kind_of?(Module)
      else
        parts_mod = Module.new
        mod.const_set(cls_name, parts_mod)
      end
      parts_mod.module_eval {
        include Amrita
        include PartsTemplate
        include HtmlCompiler::RuntimeRoutines
        extend HtmlCompiler::RuntimeRoutines
      }
      compile_partstemplate(parts_mod, e, cls_name)
    end

    private

    def compile_partstemplate(mod, template, cls_name)
      src = @cache_manager.cache(cache_path, :source, source_mtime, cls_name) do 
        f = setup_formatter
        c = HtmlCompiler::Compiler.new(f, "formatter", "context", "ptx")
        c.delete_id = false if keep_id
        c.expand_attr = expand_attr
        c.debug_compiler = debug_compiler
        c.debug_compiler = true
        method_src = c.compile(template, (HtmlCompiler::AnyData.new)).join("\n") +
          "\n# generated by PartsTemplate::compile_partstemplate at #{Time.new}\n"
        ret = <<-END
          @@parts_template = #{template.to_ruby}
          def parts_template
            @@parts_template
          end

          #{c.const_def_src.join("\n")}
          def amrita_expand_and_format(element, context, formatter)
            if element.tagname_symbol == :span and element.attrs.size == 0
              amrita_expand_and_format1(element, context, formatter)
            else
              formatter.format_element(element) do 
                amrita_expand_and_format1(element, context, formatter)
              end 
            end
          end

          def amrita_expand_and_format1(element, context, formatter)
            #{method_src}
          end
        END
        ret
      end
      mod.module_eval src.untaint
    end
  end
end
