require 'plezi/router/route'
require 'plezi/router/errors'
require 'plezi/router/assets'
require 'plezi/router/adclient'

module Plezi
   module Base
      # this module is incharge of routing requests to the correct Controller.
      module Router
         @routes = []
         @app = nil

         module_function

         # Creates a new router
         def new(app)
            if app && app != call_method
               puts "Plezi #{ Plezi::VERSION } as Middleware"
               @app = app
            end
            Plezi.app
         end

         # called when an HTTP request had arrived
         def call(env)
            request = Rack::Request.new(env)
            response = Rack::Response.new
            ret = nil
            @routes.each { |route| ret = route.call(request, response); break if ret }
            unless ret
               return @app.call(env) if @app
               ret = ::Plezi::Base::Err404Ctrl.new._pl_respond(request, response, request.params)
            end
            response.write(ret) if ret.is_a?(String)
            return response.finish
         rescue => e
            puts e.message, e.backtrace
            response = Rack::Response.new
            response.write ::Plezi::Base::Err500Ctrl.new._pl_respond(request, response, request.params)
            return response.finish
         end

         # returns the `call` method. Used repeatedly in middleware mode and only once in application mode.
         def call_method
            @call_method ||= Plezi::Base::Router.method(:call)
         end

         # Creates a new route.
         #
         # `path`:: should be a string describing the route. Named parameters are allowed.
         # `controller`:: should be a Class object that will receive all the class properties of a Plezi Controller, or one of the allowed keywords.
         def route(path, controller)
            path = path.chomp('/'.freeze) unless path == '/'.freeze
            case controller
            when :client
               controller = ::Plezi::Base::Router::ADClient
            when :assets
               controller = ::Plezi::Base::Assets
               path << '/*'.freeze unless path[-1] == '*'.freeze
            when Regexp
               path << '/*'.freeze unless path[-1] == '*'.freeze
               return @routes << RouteRewrite.new(path, controller)
            end
            @routes << Route.new(path, controller)
         end

         def list
            @routes
         end

         # Returns the URL for requested controller method and paramerets.
         def url_for(controller, method_sym, params = {})
            # GET,PUT,POST,DELETE
            r = nil
            url = '/'.dup
            @routes.each do |tmp|
               case tmp.controller
               when Class
                  next if tmp.controller != controller
                  r = tmp
                  break
               when Regexp
                  nm = nil
                  nm = tmp.param_names[0] if params[tmp.param_names[0]]
                  nm ||= tmp.param_names[0].to_sym
                  url << "#{params.delete nm}/" if params[nm] && params[nm].to_s =~ tmp.controller
               else
                  next
               end
            end
            return nil if r.nil?
            case method_sym.to_sym
            when :new
               params.delete :id
               params.delete :_method
               params.delete '_method'.freeze
               params['id'.freeze] = :new
            when :create
               params['id'.freeze] = :new
               params.delete :id
               params['_method'.freeze] = :post
               params.delete :_method
            when :update
               params.delete :_method
               params['_method'.freeze] = :put
            when :delete
               params.delete :_method
               params['_method'.freeze] = :delete
            when :index
               params.delete 'id'.freeze
               params.delete '_method'.freeze
               params.delete :id
               params.delete :_method
            when :show
               raise "The URL for ':show' MUST contain a valid 'id' parameter for the object's index to display." unless params['id'.freeze].nil? && params[:id].nil?
               params.delete '_method'.freeze
               params.delete :_method
            else
               params.delete :id
               params['id'.freeze] = method_sym
            end
            names = r.param_names
            url.chomp! '/'.freeze
            url << r.prefix
            url.clear if url == '/'.freeze
            while names.any? && params[name[0]]
               url << "/#{Rack::Utils.escape params[names.shift]}"
            end
            url << '/'.freeze if url.empty?
            (url << '?') << Rack::Utils.build_nested_query(params) if params.any?
            url
         end
      end
   end
end
