#ifndef _STR_H
#define _STR_H

#include <string>
#include <sstream>
#include <cctype>
#include <cstring>
#include <regex>
//#include "base64.h"

namespace string_tools {

using namespace std;

class str_exception {
   const char * message;

public:
   str_exception(const char * message) {
      this->message = message;
   }

   const char * get_message() const {  
      return message;
   }
};

inline
string justify(const string & text) {
   stringstream ss,ss2;
   for(size_t i = 0; i< text.size(); i++) {
      if(ispunct(text[i])) {
         ss << " " << text[i];
      }
      else {
         ss << text[i];
      }
   }
   string word;
   bool first = true;
   while(ss >> word) {
      if(!first) ss2 << " ";
      else first = false;
      ss2 << word;
   }
   return ss2.str();
}

inline
bool wild_card_match(const string & str, const string & pattern)
{
   bool ret = false;
   size_t j = 0,i;
   string temp,temp2;
   if(pattern == "*")
      return true;
   if(pattern.size() != 0) {
      for(i = 0; i < str.size() && j < pattern.size(); i++) {
         if(pattern[j] == '*') {
            if(j+1 < pattern.size()) {
               if(str[i] == pattern[j+1]) {
                  string a = str.substr(i);
                  string b = pattern.substr(j+1);
                  string bb;
                  for(size_t i = 0; i < b.size(); i++) {
                     if(b[i] == '*') {
                        break;
                     }
                     bb += b[i];
                  }
                  if(a.find(bb) == 0) {
                     j++;
                     i--;
                  }
               }
            }
            ret = true;
         }
         else if(str[i] == pattern[j]) {
            j++;
            ret = true;
         }
         else {
            ret = false;
            break;
         }
      }
      if(i == str.size() && j == pattern.size()) {
         ret = true;
      }
      else if(i == str.size() && j == pattern.size()-1) {
         if(pattern[j] == '*') {
            ret = true;
         }
      }
      else {
         ret = false;
      }
   }
   return ret;
}

inline
string::size_type find_regex(const string & expr, 
                             const string & str, 
                             string::size_type & n,
                             string::size_type start = 0)
{
   //return s.find(expr,start);
   smatch m;
   regex e(expr);
   string s = str;
   n = 0;
   string::size_type ret = 0, ptr = string::npos, last = string::npos;
   do {
      ptr = string::npos;
      if(regex_search(s,m,e)) {
         ptr = (string::size_type)m.position();
         n = (string::size_type)m.length();
         ret += ptr;
         s = m.suffix();
      }
   }
   while(ret < start && ptr != string::npos);
   if(ptr == string::npos)
      ret = ptr;
   return ret;
}

inline 
bool regex_match(const string & data, const string & expr)
{
   bool ret = false;
   string::size_type n = 0;
   ret = (find_regex(expr,data,n)) != string::npos;
   return ret;
}

inline
long from_hex(const string & n) 
{
   stringstream str;
   str << n;
   long lval = 0L;
   str >> hex >> lval;
   return lval;
}

inline
int search_replace(string & str, const string & expr, const string & rep)
{
   int count = 0;
   string::size_type n;
   string::size_type ptr = find_regex(expr, str, n);
   string temp = str, temp1, temp2;
   while(ptr != string::npos) {
      temp1 = temp.substr(0,ptr);
      temp2 = temp.substr(ptr+n);
      temp = temp1 + rep + temp2;
      ptr = find_regex(temp, expr, n, ptr+n);
      count++;
   }   
   str = temp;
   return count;
}

inline
int
x2c(const string & hex0)
{
   const char h[16] = { '0', '1', '2', '3', '4', '5', '6',
                        '7', '8', '9', 'A', 'B', 'C', 'D',
                        'E', 'F' };
   const int ih[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
   const int ih2[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
                         0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
   int ret = 0;
   for(int i = 0; i < 16; i++) {
      if(h[i] == toupper(hex0[0])) {
         if(ret == 0 && ih2[i] != 0) ret = 1;
         if(ih2[i] != 0) ret *= ih2[i];
      }
      if(h[i] == toupper(hex0[1])) {
         if(ret == 0 && ih[i] != 0) ret = 1;
         if(ih[i] != 0) ret *= ih[i];
      }
   }
   return ret;
}

inline
string 
not_null(const char * value) 
{
   string ret;
   if(value != 0)
      ret = value;
   return ret;
}


inline
string 
csv_encode(const string & str) 
{
   string ret;
   for(size_t i = 0; i < str.size(); i++) {
      if(str[i] == '\"') {
         ret += "\"\"";
      }
      else {
         ret += str[i];
      }
   }
   return ret;
}


inline 
string
str_right(const string & str, size_t n) 
{
   string ret;
   if(n < str.size()) {
      ret = str.substr(str.size()-1-n);
   }
   else if(n == str.size()) {
      ret = str;
   }
   return ret;
}

inline
string
str_left(const string & str, size_t n)
{
   string ret;
   if(n < str.size()) {
      ret = str.substr(0,n-1);
   }
   else if(n == str.size()) {
      ret = str;
   }
   return ret;
}

inline
string
str_mid(const string & str, size_t index, size_t n)
{
   string ret;
   if(n < str.size()) {
      ret = str.substr(index, n);
   }
   else if(n == str.size()) {
      ret = str;
   }
   return ret;
}

inline
string
str_repeat(const char ch, const size_t n)
{
   string str;
   for(size_t i = 0; i < n; i++) 
      str += ch;
   return str;
}

inline
string
str_reverse(const string & str)
{
   string ret;
   for(size_t i = 0; i < str.size(); i++) {
      ret = str[i] + ret;
   }
   return ret;
}

/*
inline
string
base64_encode(const string & str)
{
   string ret;
   ret = b64encode(str.c_str(), str.size());
   return ret;
}

inline
string
base64_decode(const string & str)
{
   string ret;
   ret = b64decode(str.c_str(), str.size());
   return ret;
}
*/

inline
string
uuencode(const string & str)
{
   string ret;
   return ret;
}

inline
string
uudecode(const string & str)
{
   string ret;
   return ret;
}

inline
bool
is_file_ext(const string & file_name,
            const string & filter)
{
   size_t size = filter.size()+1;
   char * buffer = new char[size];
   memset(buffer, 0, size);
   strcpy(buffer, filter.c_str());
   char * ptr = strtok(buffer, "|");
   size_t len;
   bool ret = false;
   while(ptr) {
      len = strlen(ptr);

      if(len < file_name.size()) {
         if(strncmp(file_name.c_str()+file_name.size()-len, ptr, len) == 0) {
            ret = true;
            break;
         }
      }

      ptr = strtok(NULL, "|");
   }
   delete [] buffer;
   return ret;
}


inline
string replace(const string & buffer, const string & str, const string & rep)
{
   string ret = buffer;
   string::size_type p = 0;

   while((p=ret.find(str,p)) != string::npos) {
      ret = ret.substr(0, p) + rep + ret.substr(p+str.size());
      p += rep.size();
   }

   return ret;
}

template< class T >
inline
string to_string(const T & t)
{
   stringstream ss;
   ss << t;
   string temp;
   ss >> temp;
   return temp;
}

inline
char* strcasestr(const char* str, const char* tok)
{
	char* ret = 0;
	size_t i, j;
	for (i = 0; i < strlen(str); i++) {
		for (j = 0; j < strlen(tok) && (i + j) < strlen(str); j++) {
			if (tolower(str[i + j]) == tolower(tok[j])) {

			}
			else {
				break;
			}
		}
		if (j == strlen(tok)) {
			ret = (char*)& str[i];
			break;
		}
	}
	return ret;
}
inline
int strcasecmp(const char* str, const char* tok)
{
	int ret = 0;
	size_t i, j;
   return _stricmp(str,tok);
   /*
	for (i = 0; i < strlen(str); i++) {
		for (j = 0; j < strlen(tok) && (i + j) < strlen(str); j++) {
			if (tolower(str[i + j]) == tolower(tok[j])) {

			}
			else if (tolower(str[i + j]) < tolower(tok[j])) {
				ret = -1;
			}
			else if (tolower(str[i + j]) > tolower(tok[j])) {
				ret = 1;
			}
			else {
				break;
			}
		}
		if (j == strlen(tok)) {
			break;
		}
	}
	return ret;
   */
}


inline
long to_long(const string & temp)
{
   return atol(temp.c_str());
}

inline
double to_double(const string & temp)
{
   return atof(temp.c_str());
}

inline
bool to_bool(const string & temp) 
{
   bool ret;
   if (temp == "true") {
	   ret = true;
   }
   else if (temp == "false") {
	   ret = false;
   }
   else {
	   throw str_exception("can't convert to bool data type...");
   }
   return ret;
}

inline
bool ends_with(const string & str, const string & end) {
   bool ret = false;
   if (str.size() > end.size()) {
	   string::size_type ptr = str.rfind(end);
	   if (ptr != string::npos) {
		   if (ptr == str.size() - end.size()) {
			   ret = true;
		   }
	   }
   }
   return ret;
}

inline
char* Strdup(const char* str) {
	char* ret = new char[strlen(str) + 1];
	strcpy(ret, str);
	return ret;
}

inline
bool starts_with(const string & str, const string & end) {
   bool ret = false;
   if (str.size() > end.size()) {
	   string::size_type ptr = str.find(end);
	   if (ptr != string::npos) {
		   if (ptr == 0) {
			   ret = true;
		   }
	   }
   }
   return ret;
}

inline
string trim(const string & str)
{
   string ret,temp;
   stringstream s;
   s << str;
   if (s.str().size()) {
	   while (s >> temp) {
		   if (ret.size() && temp != ",")
			   ret += " ";
		   ret += temp;
	   }
   }
   return ret;
}

template< class T >
inline
string to_hex(const T & val)
{
   stringstream s;
   s << hex << val;
   string ret;
   s >> ret;
   return ret;
}

inline
string url_escape(const string & str)
{
   string ret;
   stringstream s;
   string table = "abcdefhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
   for(size_t i = 0; i < str.size(); i++) {
      if(str[i] == ' ') 
         s << "+";
      else if(table.find(str[i]) != string::npos)
         s << str[i];
      else
         s << "%" << hex << (int)str[i]; 
   }
   ret = s.str();
   return ret;
}

inline
string strip_html(const string & html) {
   string ret;
   bool in = false;
   for(size_t i = 0; i < html.size(); i++) {
      if(html[i] == '<') 
         in = true;
      else if(html[i] == '>') {
         in = false;
         ret += ' ';
         continue;
      }
      if(!in) {
         ret += html[i];
      }
   }
   return ret;
}

inline
string 
page_encode(const string & str)
{
   stringstream ss;
   for(size_t i = 0; i < str.size(); i++) {
      if(str[i] == '<') {
         ss << "&lt;";
      }
      else if(str[i] == '>') {
         ss << "&gt;";
      }
      else if(str[i] == '&') {
         ss << "&amp;";
      }
      else if(str[i] == '\"') {
         ss << "&quot;";
      }
      else if(str[i] == ';') {
         ss << "&semi;";
      }
      else {
         ss << str[i];
      }
   }
   return ss.str();
}

inline
string to_lower(const string & str) 
{
   string ret = str;
   for(size_t i = 0; i < str.size(); i++) {
     ret[i] = tolower(str[i]);
   }
   return ret;
}

inline
string to_upper(const string & str) 
{
   string ret = str;
   for(size_t i = 0; i < str.size(); i++) {
     ret[i] = toupper(str[i]);
   }
   return ret;
}

inline
string csv_escape(const string & str) 
{
   string ret;
   for(size_t i = 0; i < str.size(); i++) {
      if(str[i] == '\"') {
         ret += "\"\"";
      }
      else {
         ret += str[i];
      }
   }
   return ret;
}

inline
string clean_text(const string & str) {
   string ret;
   for(size_t i = 0; i < str.size(); i++) {
      if(ispunct(str[i]) || isalnum(str[i]) || isspace(str[i])) {
         ret += str[i];
      }
   }
   return ret;
}

inline
bool is_digit(char ch)
{
   bool ret = false;
   const char * table = "0123456789";
   if(strchr(table, ch) != 0) 
      ret = true;
   return ret;
}

inline
bool is_letter(char ch) 
{
   bool ret = false;
   if((ch >= 'a' && ch <= 'z')
      || (ch >= 'A' && ch <= 'Z')) {
      ret = true;
   }
   return ret;
}

inline
bool is_int(const string & str)
{
   bool ret = false;
   for(size_t i = 0; i < str.size(); i++) {
      if(is_digit(str[i])) {
         ret = true;
      }
      else {
         ret = false;
         break;
      }
   }
   return ret;
}

inline
bool is_float(const string & str)
{
   bool ret = false;
   for(size_t i = 0; i < str.size(); i++) {
      if(is_digit(str[i]) || str[i] == '.') {
         ret = true;
      }
      else {
         ret = false;
         break;
      }
   }
   return ret;
}

inline
bool is_hex(char ch) 
{
   bool ret = false;
   if((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) {
      ret = true;
   }
   return ret;
}

inline
bool is_oct(char ch) 
{
   bool ret = false;
   if(ch >= '0' && ch <= '8') {
      ret = true;
   }
   return ret;
}

}


#endif /* _STR_H */
