#define CPPCMS_SOURCE
#include "filters.h"
#include "base64.h"
#include "util.h"
#include <iostream>

namespace cppcms { namespace filters {

	streamable::streamable() 
	{
		set(0,0,0,0);
	}
	streamable::streamable(streamable const &other)
	{
		set(other.ptr_,other.to_stream_,other.to_string_,other.type_);
	}
	streamable::~streamable()
	{
	}
	streamable const &streamable::operator=(streamable const &other)
	{
		if(&other!=this)
			set(other.ptr_,other.to_stream_,other.to_string_,other.type_);
		return *this;
	}
	namespace {
		void ch_to_stream(std::ostream &out,void const *p)
		{
			out<<reinterpret_cast<char const *>(p);
		}
		std::string ch_to_string(std::ios &ios,void const *p)
		{
			return reinterpret_cast<char const *>(p);
		}
		std::string s_to_string(std::ios &ios,void const *p)
		{
			return *reinterpret_cast<std::string const *>(p);
		}
	}
	streamable::streamable(char const *ptr)
	{
		set(ptr,ch_to_stream,ch_to_string,&typeid(char const *));
	}
	template<>
	streamable::streamable(std::string const &str)
	{
		set(&str,to_stream<std::string>,s_to_string,&typeid(std::string));
	}
	std::string streamable::get(std::ios &ios) const
	{
		return to_string_(ios,ptr_);
	}
	void streamable::operator()(std::ostream &out) const
	{
		to_stream_(out,ptr_);
	}
	
	void streamable::set(void const *ptr,to_stream_type tse,to_string_type tst,std::type_info const *type)
	{
		ptr_=ptr;
		to_stream_=tse;
		to_string_=tst;
		type_=type;
	}

	std::type_info const &streamable::type() const
	{
		return *type_;
	}

///////////////////////////////////
	
	struct to_upper::data {};
	to_upper::to_upper() {}
	to_upper::~to_upper() {}
	to_upper::to_upper(to_upper const &other) : obj_(other.obj_) {}
	to_upper::to_upper(streamable const &obj) : obj_(obj) {}
	to_upper const &to_upper::operator=(to_upper const &other){ obj_ = other.obj_; return *this; }
	void to_upper::operator()(std::ostream &out) const
	{
		std::string tmp =obj_.get(out) ;
		std::locale loc = out.getloc(); 
		out << ::cppcms::locale::to_upper( tmp,loc);
	}

	struct to_lower::data {};
	to_lower::to_lower() {}
	to_lower::~to_lower() {}
	to_lower::to_lower(to_lower const &other) : obj_(other.obj_) {}
	to_lower::to_lower(streamable const &obj) : obj_(obj) {}
	to_lower const &to_lower::operator=(to_lower const &other){ obj_ = other.obj_; return *this; }
	void to_lower::operator()(std::ostream &out) const
	{
		out << locale::to_lower(obj_.get(out),out.getloc());
	}

	struct to_title::data {};
	to_title::to_title() {}
	to_title::~to_title() {}
	to_title::to_title(to_title const &other) : obj_(other.obj_) {}
	to_title::to_title(streamable const &obj) : obj_(obj) {}
	to_title const &to_title::operator=(to_title const &other){ obj_ = other.obj_; return *this; }
	void to_title::operator()(std::ostream &out) const
	{
		out << locale::to_title(obj_.get(out),out.getloc());
	}

	struct escape::data {};
	escape::escape() {}
	escape::~escape() {}
	escape::escape(escape const &other) : obj_(other.obj_) {}
	escape::escape(streamable const &obj) : obj_(obj) {}
	escape const &escape::operator=(escape const &other){ obj_ = other.obj_; return *this; }
	void escape::operator()(std::ostream &out) const
	{
		out << util::escape(obj_.get(out));
	}

	struct urlencode::data {};
	urlencode::urlencode() {}
	urlencode::~urlencode() {}
	urlencode::urlencode(urlencode const &other) : obj_(other.obj_) {}
	urlencode::urlencode(streamable const &obj) : obj_(obj) {}
	urlencode const &urlencode::operator=(urlencode const &other){ obj_ = other.obj_; return *this; }
	void urlencode::operator()(std::ostream &out) const
	{
		out << util::urlencode(obj_.get(out));
	}

	struct raw::data {};
	raw::raw() {}
	raw::~raw() {}
	raw::raw(raw const &other) : obj_(other.obj_) {}
	raw::raw(streamable const &obj) : obj_(obj) {}
	raw const &raw::operator=(raw const &other){ obj_ = other.obj_; return *this; }
	void raw::operator()(std::ostream &out) const
	{
		out << obj_;;
	}

	struct base64_urlencode::data {};
	base64_urlencode::base64_urlencode() {}
	base64_urlencode::~base64_urlencode() {}
	base64_urlencode::base64_urlencode(base64_urlencode const &other) : obj_(other.obj_) {}
	base64_urlencode::base64_urlencode(streamable const &obj) : obj_(obj) {}
	base64_urlencode const &base64_urlencode::operator=(base64_urlencode const &other){ obj_ = other.obj_; return *this; }
	void base64_urlencode::operator()(std::ostream &os) const
	{
		std::string const s=obj_.get(os);
		using namespace cppcms::b64url;
		unsigned char const *begin=reinterpret_cast<unsigned char const *>(s.c_str());
		unsigned char const *end=begin+s.size();
		std::vector<unsigned char> out(encoded_size(s.size())+1);
		encode(begin,end,&out.front());
		out.back()=0;
		char const *buf=reinterpret_cast<char const *>(out.front());
		os<<buf;
	}

	struct date::data {};
	struct time::data {};
	struct datetime::data {};

	date::date() : time_(0) {}
	datetime::datetime() : time_(0){}
	time::time() : time_(0) {}
	
	date::~date() {}
	datetime::~datetime() {}
	time::~time() {}
	
	date::date(date const &other) : time_(other.time_) {}
	time::time(time const &other) : time_(other.time_) {}
	datetime::datetime(datetime const &other) : time_(other.time_) {}

	date const &date::operator=(date const &other) { time_=other.time_; return *this; }
	time const &time::operator=(time const &other) { time_=other.time_; return *this; }
	datetime const &datetime::operator=(datetime const &other) { time_=other.time_; return *this; }

	date::date(double t) : time_(t) {}
	time::time(double t) : time_(t) {}
	datetime::datetime(double t) : time_(t) {}

	void date::operator()(std::ostream &out) const
	{
		out << format("{1,date}") % time_;
	}
	
	void time::operator()(std::ostream &out) const
	{
		out << format("{1,time}") % time_;
	}
	
	void datetime::operator()(std::ostream &out) const
	{
		out << format("{1,datetime}") % time_;
	}


}} // cppcms::filters
