module Skates
  module Base
    
    ##
    # Your application's controller should be descendant of this class.
    class Controller
      
      attr_accessor :stanza, :rendered, :action_name # Stanza received by the controller (Nokogiri::XML::Node)
      
      ##
      # Creates a new controller (you should not override this class) and assigns the stanza as well as any other value of the hash to instances named after the keys of the hash.
      def initialize(stanza = nil)
        @stanza = stanza
        @view = nil
      end
      
      ##
      # Performs the action and calls back the optional block argument : you should not override this function
      def perform(action)
        @action_name = action
        begin
          self.send(@action_name)
        rescue
          Skates.logger.error {
            "#{$!}:\n#{$!.backtrace.join("\n")}"
          }
        end
        self.render
      end
      
      ##
      # Returns the list of variables assigned during the action.
      def assigns
        vars = Hash.new
        instance_variables.each do |var|
          if !["@view", "@action_name", "@block"].include? var
            vars[var[1..-1]] = instance_variable_get(var)
          end
        end
        vars
      end
      
      ##
      # Called by default after each action to "build" a XMPP stanza. By default, it will use the /controller_name/action.xml.builder
      # You can use the following options :
      #   - :file : render a specific file (can be in a different controller)
      #   - :action : render another action of the current controller
      #   - :nothing : doesn't render anything
      def render(options = {}) 
        return if @view and !options[:force] # Avoid double rendering, if we have already attached a view
        
        if options == {} # default rendering
          render(:file => default_template_name)

        elsif action_name = options[:action]
          result = render(:file => default_template_name(action_name.to_s))

        elsif options[:file]
          file = options[:file]
          if file =~ /^\// # Render from view root
            @view = render_for_file(File.join("app", "views", "#{file}.xml.builder"))
          else
            @view = render_for_file(view_path(file)) 
          end

        elsif options[:nothing]
          @view = Skates::Base::View.new()
        end
      end
      
      ##
      # Actually evaluates the view
      def evaluate
        @view.evaluate if @view
      end
      
      ##
      # Render and evaluate a given view
      def render_and_evaluate(options = {})
        render(options)
        evaluate
      end
      
      ##
      # Renders, evaluate and sends
      def render_evaluate_and_send(options)
        response = render_and_evaluate(options)
        Skates.router.connection.send_xml response 
      end
      
      protected
      
      ##
      # Builds the view path.
      def view_path(file_name)
        File.join("app", "views", "#{self.class.name.gsub("Controller","").downcase}", file_name)
      end
      
      ##
      # Default template name used to build stanzas
      def default_template_name(action_name = nil)
        "#{action_name || @action_name}.xml.builder"
      end
      
      ##
      # Creates the view and "evaluates" it to build the XML for the stanza
      def render_for_file(file)
        Skates.logger.info {
          "RENDERING : #{file}"
        }
        Skates::Base::View.new(file, assigns)
      end
      
    end
  end
end
