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

#include <cppcms/defs.h>
#include <booster/noncopyable.h>
#include <cppcms/base_view.h>
#include <cppcms/cppcms_error.h>

#include <booster/auto_ptr_inc.h>
#include <map>
#include <vector>
#include <ostream>

namespace cppcms {

	namespace json { class value; }

	///
	/// \brief this namespace holds all classes used for rendering CppCMS views.
	///
	namespace views {

		///
		/// \brief The class that represents a single skin and generates its views.
		///
		/// Usually used by templates compiler
		///
		class CPPCMS_API generator : public booster::noncopyable {
		public:
			/// The callback that creates a single view
			typedef std::auto_ptr<base_view> view_factory_type(std::ostream &,base_content *c);

			generator();
			~generator();

			///
			/// Add a single view of type \a View that uses content of type \a Content
			/// Using name \a view_name.
			///
			/// If \a safe is true that dynamic cast is used to ensure that content has proper type
			/// otherwise static cast.
			///
			/// Usually used by templates generator
			///
			template<typename View,typename Content>
			void add_view(std::string const &view_name,bool safe = true)
			{
				view_factory_type *factory = 0;
				if(safe)
					factory = view_builder<View,Content>;
				else
					factory = unsafe_view_builder<View,Content>;
				add_factory(view_name,factory);
			}
			
			///
			/// Add a view that uses a callback
			///	
			void add_factory(std::string const &name,view_factory_type *factory);
			///	
			/// Get skin name
			///
			std::string name() const;
			///
			/// Set skin name
			///
			void name(std::string const &n);
			///
			/// Create a view by its name that writes that data to \a outout using
			/// a content \a content.
			///
			std::auto_ptr<base_view> create(std::string const &view_name,
							std::ostream &output,
							base_content *content) const;
			///
			/// Enumerate view names
			///
			std::vector<std::string> enumerate() const;
		private:
			
			template<typename View,typename Content>
			static std::auto_ptr<base_view> view_builder(std::ostream &stream,base_content *c) 
			{
				std::auto_ptr<base_view> p;
				
				try {
					p.reset(new View(stream,dynamic_cast<Content &>(*c)));
				}
				catch(std::bad_cast const &) {
					throw cppcms_error("cppcms::views::generator: an attempt to use content of invalid type");
				}
				return p;
			}
			
			template<typename View,typename Content>
			static std::auto_ptr<base_view> unsafe_view_builder(std::ostream &stream,base_content *c) 
			{
				std::auto_ptr<base_view> p(new View(stream,static_cast<Content &>(*c)));
				return p;
			}
			

			struct data;
			typedef std::map<std::string,view_factory_type *> views_type;
			views_type views_;
			std::string name_;
			booster::hold_ptr<data> d;
		};
		
		///
		/// \brief A class that allows to use the view withing the internal lock used inside pool class
		///
		/// It is similar in its operation in creating the view class similarly to pool::render() but
		/// not calling base_view::render member function.
		///
		/// It is used with `<% using ... from ... %>` CppCMS template
		///
		/// \ver{v1_2} 
		class CPPCMS_API view_lock : public booster::noncopyable {
		public:
			///
			/// Create a view and lock pool's internal lock
			///
			view_lock(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content);
			///
			/// Delete the view and unlock the pool's lock
			///
			~view_lock();
			///
			/// Shortcut to dynamic_cast<View &>(view())
			///
			template<typename View>
			View &use_view()
			{
				return dynamic_cast<View &>(view());
			}
			///
			/// Get the underlying view object
			///
			base_view &view();
		private:
			struct _data;
			booster::hold_ptr<base_view> view_;
			booster::hold_ptr<_data> d;
		};

		///
		/// \brief This is a singleton object that holds all views in the process. Any view
		/// is registered and unregistered via this object.
		///
		/// It is usually not used directly
		///
		class CPPCMS_API pool : public booster::noncopyable {
		public:
			///
			/// Add new skin to pool
			///
			/// This function is thread safe
			///
			void add(generator const &generator);
			///
			/// Remove the skin from pool
			///
			/// This function is thread safe
			///
			void remove(generator const &generator);
		
			///
			/// Render Skin
			///	
			/// This member function is used to render templates. Generally you should not use
			/// it directly, unless you have very good reasons.
			///
			/// \param skin - the name of the skin that should be used
			/// \param template_name - the name of template (class) that should be rendered.
			/// \param out - the output stream into which the view should be rendered
			/// \param content - the content that should be rendered using this view.
			///
			/// This function is thread safe
			///
			void render(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content);

			///
			/// Get all loaded views
			///
			/// This function is thread safe
			///
			/// \ver{v1_2}
			std::vector<std::string> enumerate();
			
			///
			/// Get the singleton instance of the views pool
			///
			static pool &instance();
		
		private:
			friend class view_lock;
			void lock();
			void unlock();

			// called on locked object
			base_view *create_view(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content);
			pool();
			~pool();

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

		///
		/// \brief This class controls the views used my application it knows to load them dynamically
		/// and reload if needed
		///
		class CPPCMS_API manager : public booster::noncopyable {
		public:
			///
			/// Create new views manager
			///
			/// Usually created by cppcms::service()
			///
			manager(json::value const &settings);
			~manager();

			///
			/// Render a template in a skin. Checks if any of shared objects/dlls should be reloaded
			///
			void render(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content);
			///
			/// Get default skin
			///
			std::string default_skin();
		private:
			struct data;
			booster::hold_ptr<data> d;
		};
	} // views

}


#endif
