///////////////////////////////////////////////////////////////////////////////
//                                                                             
//  Copyright (C) 2008-2012  Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>     
//                                                                             
//  See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef CPPCMS_URL_MAPPER_H
#define CPPCMS_URL_MAPPER_H

#include <cppcms/defs.h>
#include <cppcms/string_key.h>
#include <booster/noncopyable.h>
#include <booster/hold_ptr.h>
#include <cppcms/filters.h>
#include <string>
#include <vector>


namespace cppcms {
	class application;

	///
	/// \brief class for mapping URLs - the opposite of dispatch
	///
	/// This class is useful for mapping between different page identifications
	/// that represent different classes/view and the URL.
	///
	///
	/// The URL mapping is done in hierarchy of applications where each application
	/// has its own name and they bundler into single hierarchy. Each application in hierarchy
	/// can be referred by its key and location. It may have different "urls" for mapping
	/// different values.
	///
	/// For example, we develop a content management system with following tools:
	///
	/// - site
	///     - news 
	///         - article 
	///         - category 
	///     - forums
	///         - topics
	///         - threads
	///     - users
	///         - management
	///         - registration
	///         - profile
	///
	/// Each node above is represented my cppcms::application and its children mounted
	/// to it. In order to access each URL we use "file system" like convention:
	///
	///  - "/" - the default site page
	///  - "/news" - the default news page
	///  - "/news/article" - the default article page
	///  - "/news/category/by_date" - the categories pages sorted by data
	///  - "/news/category/by_interest" - the categories pages sorted by interest
	///  - "/news/category" - the default category pages
	///
	/// And so on.
	///
	/// Each application can be referred by its full path from root or by its relative path,
	/// so if we for example in article sub-application and we want to refer to "category"
	/// URL we would use something like map(out(),"../category/by_interest",category_id);
	///
	/// In order to all this process work, each application should mount its children by some name,
	/// like:
	///
	/// \code
	///     site::site(cppcms::service &s) : 
	///           cppcms::application(s),
	///           news_(s),
	///           forums_(s),
	///           users_(s)
	///     {
	///         add(news_);
	///         mapper().mount("news","/news{1}",news_);
	///         add(forums_);
	///         mapper().mount("forums","/forums{1}",forums_);
	///         ...
	/// \endcode
	///
	/// You can also use cppcms::application::add and cppcms::application::attach that allows
	/// to provide mapping for url_dispatcher and url_mapper in a single command like:
	///
	/// \code
	///    add(news_,
	///        "news","/news{1}",
	///        "/news((/.*)?)",1);
	/// \endcode
	///
	/// Which effectively the same as:
	///
	/// \code
	///    add(news_);
	///    mapper().mount("news","/news{1}",news_);
	///    dispatcher().mount("/news((/.*)?)",news_,1);
	/// \endcode
	///
	/// Such system allows using "url" tag in templates system easily as"
	/// 
	/// \code
	///   <a href="<% url "/news/article" using article id %>" >...</a>
	/// \endcode
	///
	/// Each mounted application may a default URL (something like index.html)
	/// which is mapped when mounted application is referred. So for example there
	/// are may be following URLs:
	///
	/// - "/news/article" or "/news/article/" - the default URL
	/// - "/news/article/preview" - preview unpublished article URL.
	///
	/// They can be defined in article class as following:
	///
	/// \code
	///  article::article(cppcms::service &s) : cppcms::application(s)
	///  {
	///     mapper().assign("/{1}"); // the default URL
	///     dispatcher().assign("/(\\d+)",&article::display,this,1);
	///     mapper().assign("preview","/{1}/preview"); // the preview URL
	///     dispatcher().assign("/(\\d+)/preview",&article::preview,this,1);
	///  }
	/// \endcode
	///
	/// Additional supported feature is "named" parameters which are usually set to
	/// some default value using set_value, they are defined by special keywords between
	/// instead of numbers. 
	///
	/// For example assign("article", "/article/{lang}/{1}") were "lang" is special value for language
	/// defined by set_value("lang","en"), so mapping map(out(),"article",10) would create
	/// a URL "/article/en/10"
	///
	/// Sometimes it is useful to change such values there are two ways to do it:
	///
	/// -    Overloading keyword with different parameters number assign("article","/article/{1}/{2}")
	///      and then calling map(out(),"article","ru",10) for URL like "/article/ru/10".
	/// -    Using naming of parameters at mapping level by prepending comma separated keywords at the
	///      end of the path line after ";" map(out(),"article;lang","ru",10) - which would effectively work
	///      like temporary calling set_value("lang","ru") and then calling map(out(),"article",10).
	///      Unlike the previous case it also allows to do such changes globally.
	///      <br>
	///      For example if "news" application mounts article using "/{lang}/{1}" then
	///      using such mapping would affect top level URL that does not belong to specific application.
	///
	///
	///
	/// 
	///
	///
	class CPPCMS_API url_mapper : public booster::noncopyable {
	public:
		/// \cond INTERNAL
		url_mapper(application *app);
		~url_mapper();
		/// \endcond
		
		///
		/// Get the root of the application - the string that
		/// is added to the any URL patter like "/forum" or
		/// "http://my.site.com"
		///
		std::string root();
		///
		/// Set the root of the application - the string that
		/// is added to the any URL patter like "/forum" or
		/// "http://my.site.com"
		///
		void root(std::string const &r);

		///
		/// Provide a mapping between special \a key and a \a url pattern.
		///
		/// URL patter is a string that includes mapped patters between "{" and "}"
		/// brackets. For example "/page/{1}" where "{1}" is being substituted
		/// by the first parameter in map functions.
		///
		/// The ids can be numbers - 1 to 6 and special keys that can be changed
		/// in the run time using set_value functions. For example:
		///
		/// "/wiki/{lang}/page/{1}"
		///
		/// Where "lang" can be defined by "set_value". For example.
		///
		/// For the url above with "lang" set to "en" and first parameter "cppcms"
		/// the string would be "/wiki/en/page/cppcms"
		///
		/// Note the keys may be overloaded by number of parameters as for example:
		///
		/// - <tt>assign("page","/wiki/{1}/page/{2}");</tt>
		/// - <tt>assign("page","/wiki/{lang}/page/{1}");</tt>
		/// - <tt>assign("page","/wiki/{lang}/page/main");</tt>
		///
		/// Then map(output,"page") - would create "/wiki/en/page/main", 
		/// map(output,"page",134) would create "/wiki/en/page/132" and 
		/// map(output,"page","ru","cppcms") would create "/wiki/ru/page/cppcms"
		///
		/// Note: They keys containing "/", "," or ";" and keys with values "..", ".", ""  are prohibited  
		/// as they have special meanings
		///
		void assign(std::string const &key,std::string const &url);
		///
		/// Map the default key for the application, \a url has same rules as for assign(key,url) but
		/// they rather refer to default application's URL when it is used in hierarchy.
		///
		void assign(std::string const &url);

		///
		/// Set special value for a key that would be used
		/// in URL mapping, for example set_value("lang","en")
		///
		/// Note: this value is defined globally for all applications hierarchy and not only
		/// for this specific application
		///
		void set_value(std::string const &key,std::string const &value);
		///
		/// Clear the special value - reset to empty
		///
		/// Note: this value is cleared globally for all applications hierarchy and not only
		/// for this specific application
		///
		void clear_value(std::string const &key);
	
		///
		/// Write the URL to output stream \a out for the URL \a path with 0 parameters
		///	
		void map(	std::ostream &out,
				char const *path);

		///
		/// Write the URL to output stream \a out for the URL \a path with 1 parameters
		///	
		void map(	std::ostream &out,
				char const *path,
				filters::streamable const &p1);

		///
		/// Write the URL to output stream \a out for the URL \a path with 2 parameters
		///	
		void map(	std::ostream &out,
				char const *path,
				filters::streamable const &p1,
				filters::streamable const &p2);

		///
		/// Write the URL to output stream \a out for the URL \a path with 3 parameters
		///	
		void map(	std::ostream &out,
				char const *path,
				filters::streamable const &p1,
				filters::streamable const &p2,
				filters::streamable const &p3);

		///
		/// Write the URL to output stream \a out for the URL \a path with 4 parameters
		///	
		void map(	std::ostream &out,
				char const *path,
				filters::streamable const &p1,
				filters::streamable const &p2,
				filters::streamable const &p3,
				filters::streamable const &p4);
		///
		/// Write the URL to output stream \a out for the URL \a path with 5 parameters
		///	
		void map(	std::ostream &out,
				char const *path,
				filters::streamable const &p1,
				filters::streamable const &p2,
				filters::streamable const &p3,
				filters::streamable const &p4,
				filters::streamable const &p5);
		///
		/// Write the URL to output stream \a out for the URL \a path with 6 parameters
		///	
		void map(	std::ostream &out,
				char const *path,
				filters::streamable const &p1,
				filters::streamable const &p2,
				filters::streamable const &p3,
				filters::streamable const &p4,
				filters::streamable const &p5,
				filters::streamable const &p6);
		///
		/// Write the URL to output stream \a out for the URL \a path with 0 parameters
		///	
		void map(	std::ostream &out,
				std::string const &path);

		///
		/// Write the URL to output stream \a out for the URL \a path with 1 parameters
		///	
		void map(	std::ostream &out,
				std::string const &path,
				filters::streamable const &p1);

		///
		/// Write the URL to output stream \a out for the URL \a path with 2 parameters
		///	
		void map(	std::ostream &out,
				std::string const &path,
				filters::streamable const &p1,
				filters::streamable const &p2);

		///
		/// Write the URL to output stream \a out for the URL \a path with 3 parameters
		///	
		void map(	std::ostream &out,
				std::string const &path,
				filters::streamable const &p1,
				filters::streamable const &p2,
				filters::streamable const &p3);

		///
		/// Write the URL to output stream \a out for the URL \a path with 4 parameters
		///	
		void map(	std::ostream &out,
				std::string const &path,
				filters::streamable const &p1,
				filters::streamable const &p2,
				filters::streamable const &p3,
				filters::streamable const &p4);
		///
		/// Write the URL to output stream \a out for the URL \a path with 5 parameters
		///	
		void map(	std::ostream &out,
				std::string const &path,
				filters::streamable const &p1,
				filters::streamable const &p2,
				filters::streamable const &p3,
				filters::streamable const &p4,
				filters::streamable const &p5);
		///
		/// Write the URL to output stream \a out for the URL \a path with 6 parameters
		///	
		void map(	std::ostream &out,
				std::string const &path,
				filters::streamable const &p1,
				filters::streamable const &p2,
				filters::streamable const &p3,
				filters::streamable const &p4,
				filters::streamable const &p5,
				filters::streamable const &p6);

		///
		/// Mount sub application \a app using name \a name to \a url.
		///
		/// The URL format as in assign but it requires a single parameter {1}
		/// which would be substituted with the mapping of the URL of sub-application
		/// instead of using "root" patch
		///
		void mount(std::string const &name,std::string const &url,application &app);
		///
		/// Get a mapper of mounted application by its name
		///
		url_mapper &child(std::string const &name);

		///
		/// Get a parent mapper, if not exists throws cppcms_error
		///
		url_mapper &parent();
		///
		/// Get a topmost mapper, if have no parents returns reference to \c this.
		///
		url_mapper &topmost();

	private:
		void real_assign(std::string const &key,std::string const &url,application *child = 0);
		url_mapper &get_mapper_for_key(string_key const &key,string_key &real_key,std::vector<string_key> &direct);
		url_mapper *root_mapper();
		void real_map(	char const *key,
				filters::streamable const *const *params,
				size_t params_no,
				std::ostream &output);

		struct data;
		booster::hold_ptr<data> d;
	};

};

#endif
