# encoding: UTF-8

module Spontaneous::Model::Page
  module Controllers
    extend Spontaneous::Concern

    ACTION_SEPARATOR = "@".freeze

    module ClassMethods
      def controllers
        @controllers ||= Spontaneous::Collections::PrototypeSet.new(supertype, :controllers)
      end

      def default_controller_base_class
        Spontaneous::Rack::PageController
      end

      # Searches through the type heirarchy for the first defined controller
      # if there isn't one for the given namespace it tries on the 'default'
      # namespace
      def controller_superclass(namespace, base_class)
        return base_class unless base_class.nil?
        return ::PageController if defined?(::PageController)
        search = ancestors.select { |klass| klass.respond_to?(:controllers) }
        controller_superclass = [namespace, :__nil__].uniq.flat_map { |n|
          search.map { |type| type.controllers[n] }
        }.compact.first
        controller_superclass ||= default_controller_base_class
      end

      def controller(namespace = :__nil__, base_class = nil, &block)
        namespace_name = (namespace == :__nil__ ? "Root" : namespace.to_s.camelize)
        controller_class = Class.new(controller_superclass(namespace, base_class))
        const_set("#{namespace_name}Controller", controller_class)# unless self.const_defined?(controller_class_name)
        controller_class.class_eval(&block) if block_given?
        controllers[namespace] = controller_class
      end

      METHOD_MAP = Hash[%w(GET PUT POST DELETE HEAD OPTIONS PATCH LINK UNLINK).map { |m| [m, m.downcase.to_sym] }].freeze

      def normalize_method(method)
        return method if method.is_a?(Symbol)
        METHOD_MAP[method]
      end

      # Tests for existance of a request handler for a method
      # Used by the publishing mechanism to determine which template bucket a
      # published page should be placed in.
      def dynamic?(method = :get)
        method = normalize_method(method)
        return true unless method == :get
        controller = controllers[:__nil__]
        controller and controller.dynamic?(method)
      end
    end # ClassMethods

    # InstanceMethods

    def dynamic?(method = :get)
      self.class.dynamic?(method)
    end

    # resolve and call the relevant action handler and return the results to the controller
    def process_action(site, action_path, env, format)
      namespace, *parts = action_path.split(S::Constants::SLASH)
      path = "/" << parts.join(S::Constants::SLASH)
      env[S::Constants::PATH_INFO] = path
      run_controller(site, namespace, env, format)
    end

    def process_root_action(site, env, format)
      env[S::Constants::PATH_INFO] = S::Constants::SLASH
      run_controller(site, :__nil__, env, format)
    end

    def run_controller(site, namespace, env, format)
      controller_class = self.class.controllers[namespace.to_sym]
      return 404 if controller_class.nil?
      app = controller_class.new(site, self, format)
      status, headers, body = app.call(env)
      [status, headers, body]
    end

    # generate an action URL of the form
    # <path to page>/@<action namespace>/<action path>
    def action_url(namespace, path = nil)
      [self.path, "#{ACTION_SEPARATOR}#{namespace}", path].compact.join(S::Constants::SLASH).gsub(%r{//}, '/')
    end
  end # Controllers
end