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

#ifndef CPPCMS_IMPL_REWRITE_H
#define CPPCMS_IMPL_REWRITE_H

#include <cppcms/defs.h>
#include <booster/regex.h>
#include <cppcms/json.h>
#include <cppcms/cppcms_error.h>

#include "string_map.h"

namespace cppcms {
namespace impl {

class url_rewriter {
	struct rule {
		rule(std::string const &r,std::string const &pat,bool fin=true) :
			expression(r),
			final(fin)
		{
			size_t pos = 0;
			bool append = false;
			for(;;){
				size_t start = pos;
				pos = pat.find('$',pos);
				std::string subpat = pat.substr(start,pos-start);
				if(append)
					pattern.back().append(subpat);
				else
					pattern.push_back(subpat);
				if(pos==std::string::npos) 
					break;
				pos++;
				char c;
				if(pos >= pat.size() || ((c=pat[pos++])!='$' && c < '0' && '9' < c)) 
					throw cppcms_error("Invalid rewrite pattern :" + pat);
				if(c=='$') {
					pattern.back()+='$';
					append=true;
				}
				else {
					index.push_back(c-'0');
					append=false;
				}

			}
			pattern_size = 0;
			for(size_t i=0;i<pattern.size();i++)
				pattern_size+=pattern[i].size();
		}
		booster::regex expression;
		std::vector<std::string> pattern;
		std::vector<int> index;
		size_t pattern_size;
		bool final;

		char *rewrite_once(booster::cmatch const &m,string_pool &pool) const
		{
			size_t total_size = pattern_size;
			for(size_t i=0;i<index.size();i++) {
				total_size += m[index[i]].length();
			}
			char *new_url = pool.alloc(total_size+1);
			char *ptr = new_url;
			for(size_t i=0;i<index.size();i++) {
				ptr = std::copy(pattern[i].begin(),pattern[i].end(),ptr);
				ptr = std::copy(m[index[i]].first,m[index[i]].second,ptr);
			}
			ptr = std::copy(pattern.back().begin(),pattern.back().end(),ptr);
			*ptr = 0;
			return new_url;
		}
	};
public:
	url_rewriter(json::array const &ar)
	{
		rules_.reserve(ar.size());
		for(size_t i=0;i<ar.size();i++) {
			std::string r = ar[i].get<std::string>("regex");
			std::string p = ar[i].get<std::string>("pattern");
			bool final = ar[i].get("final",true);
			rules_.push_back(rule(r,p,final));
		}
	}
	char *rewrite(char *url,string_pool &pool) const
	{
		booster::cmatch m;
		for(size_t i=0;i<rules_.size();i++) {
			rule const &r = rules_[i];
			if(booster::regex_match(url,m,r.expression)) {
				url = r.rewrite_once(m,pool);
				if(r.final)
					break;
			}
		}
		return url;
	}
private:
	std::vector<rule> rules_;	
};

} // impl
} // cppcms
#endif
