module Rad::Controller::Abstract::Render      
  SPECIAL_FORMAT_HANDLERS = {
    json: lambda{|o| o.to_json},
    xml: lambda{|o| o.to_xml},
    js: lambda{|o| o},        
  }

  def _layout *args            
    @_layout = *args unless args.empty?
    @_layout
  end
  def _layout= layout
    @_layout = layout
  end

  def render *args
    options = rad.template.parse_arguments *args        
    
    instance_variables = {      
      controller_name: self.class.controller_name
    }

    instance_variable_names.each do |name|
      instance_variables[name[1..-1]] = instance_variable_get name unless name =~ /^@_/
    end
    
    instance_variables[:action_name] = options[:action] if options[:action]
    
    context = self.class.context_class.new(instance_variables, self)            
     
    options.reverse_merge! \
      context: context,
      format: params.format,
      exact_format: true,      
      relative_path_resolver: self.class

    content = render_content options
    content = render_layout content, options

    throw :halt, content
  end

  protected
    def render_content options      
      if special_format = SPECIAL_FORMAT_HANDLERS.keys.find{|f| options.include? f}            
        handler = SPECIAL_FORMAT_HANDLERS[special_format]
        if special_format.to_s != params.format
          raise "You trying responing with '#{special_format}' to the '#{params.format}' requested format!"
        end
        handler.call options[special_format]
      elsif options.include? :inline            
        options[:inline]
      elsif options[:template] == :nothing
        ''
      else                  
        if action = options.delete(:action)          
          options[:template].must_be.blank          
          options[:template] = action
          
          options.reverse_merge! \
            prefixes: [''],
            if_not_exist: '/rad_default_templates/blank_template'
        end
        options[:template].must_be.present
        options[:template] = options[:template].to_s                      
        
        rad.template.render options
      end
    end
      
    def render_layout content, options
      layout = options.include?(:layout) ? options[:layout] : self._layout
      
      if layout and rad.template.exist? layout, options        
        options = options.merge template: layout
        rad.template.render options do |*args|
          if args.empty?
            content
          else
            args.size.must_be == 1
            variable_name = args.first.to_s
            options[:context].content_variables[variable_name]
          end
        end        
      else
        content
      end
    end

  module ClassMethods
    def layout layout, options = {}
      before options do |controller|
        controller._layout = layout
      end
    end
    
    def context_class
      unless @context_class
        parent_context_class = nil
        ancestors[1..-1].each do |anc|            
          break if parent_context_class = anc.respond_to(:context_class)
        end
        parent_context_class ||= Rad::Controller::Context

        class_name = "#{self.name}::#{self.name.split('::').last}Context"

        # raise "Tempate context #{class_name} already defined!" if Object.const_defined? class_name
        eval "class #{class_name} < #{parent_context_class}; end", TOPLEVEL_BINDING, __FILE__, __LINE__
        @context_class = class_name.constantize
      end
      @context_class
    end
    
    def find_relative_template *args
      return _find_relative_template *args unless rad.production?
      
      # use cache
      @relative_template ||= {}      
      unless @relative_template.include? args        
        @relative_template[args] = _find_relative_template *args
      end
      @relative_template[args]
    end
    
    def _find_relative_template tname, prefixes, format, exact_format, current_dir
      tname.must_be.present
      
      path = nil
      
      # own templates
      ["/#{controller_name.underscore}/#{tname}", "/#{controller_name.gsub('::', '/')}/#{tname}"].each do |name|         
        path ||= rad.template.find_file(name, prefixes, format, exact_format, rad.template.paths)
      end
      
      # own :actions templates      
      ["/#{controller_name.underscore}/actions", "/#{controller_name.gsub('::', '/')}/actions"].each do |name|
        unless path
          path = rad.template.find_file(name, prefixes, format, exact_format, rad.template.paths)
          path = nil if path and (File.read(path) !~ /^.*when.+[^_a-zA-Z0-9]#{tname}[^_a-zA-Z0-9].*$/)
        end
      end
      
      # superclasses templates
      unless path
        parent = ancestors[1..-1].find{|a| a.respond_to?(:find_relative_template)} #  and a.instance_methods.include?(action)
        if parent and parent != Rad::Controller::Abstract
          path = parent.find_relative_template(tname, prefixes, format, exact_format, current_dir)
        end
      end
      
      # relative template
      if !path and current_dir      
        path = rad.template.relative_path_resolver.find_relative_template(tname, prefixes, format, exact_format, current_dir)
      end
      
      return path
    end

  end
end