
#define LOCAL_DEBUG
#include "debug.h"

#include "meta.h"
#include "fileio.h"
#include <unistd.h>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>

#ifdef HAVE_WORDEXP
#include <wordexp.h>
#elif defined(HAVE_GLOB)
#include <glob.h>
#endif

using namespace MYSTD;

string sPathSep(SZPATHSEP);
string sPathSepUnix(SZPATHSEPUNIX);

/*
int getUUID() {
    lfd=(lfd+1)%65536;
   //cerr << "UUID: " << lfd <<endl;
    return lfd;
}
*/
void set_nb(int fd) {
    int flags = fcntl(fd, F_GETFL);
    //ASSERT(flags != -1);
    flags |= O_NONBLOCK;
    flags = fcntl(fd, F_SETFL, flags);
}

/*
inline tStrPos findHostStart(const MYSTD::string & sUri)
{
	tStrPos p=0, l=sUri.size();
	if (0==sUri.compare(0, 7, "http://"))
		p=7;
	while(p<l && sUri[p]=='/') p++;
	return p;
}

void trimProto(MYSTD::string & sUri)
{
	sUri.erase(findHostStart(sUri));
}
*/


mstring GetBaseName(const string &in)
{
	if(in.empty())
		return "";

	tStrPos end = in.find_last_not_of(CPATHSEP); // must be the last char of basename
	if(end == stmiss) // empty, or just a slash?
		return "/";
	
	tStrPos start = in.rfind(CPATHSEP, end);
	if(stmiss == start)
		start=0;
	
	return in.substr(start, end+1-start);
}

/*
void find_base_name(const char *in, const char * &pos, UINT &len)
{
	int l=strlen(in);
	if(l==0)
	{
		pos=in;
		len=0;
		return;
	}
	
		const char *p, *r;
		
		for(p=in+l-1;*p==cPathSep;p--)
		{	
			if(p==in)
			{
				pos=in;
				len=1;
				return;
			}
		}
		for(r=p;r>=in && *p!=cPathSep;r--)
		{
			if(r==in)
			{
				pos=in;
				len=p-in+1;
			}
		}
	
}
*/

/*!
 * \brief Simple split function, outputs resulting tokens into a string vector, with or without purging the previous contents
 */
tStrVec::size_type Tokenize(const string & in, const char *sep,
		tStrVec & out, bool bAppend, MYSTD::string::size_type nStartOffset)
{
	if(!bAppend)
		out.clear();
	tStrVec::size_type nBefore(out.size());
	
	tStrPos pos=nStartOffset, pos2=nStartOffset, oob=in.length();
	while (pos<oob)
	{
		pos=in.find_first_not_of(sep, pos);
		if (pos==stmiss) // no more tokens
			break;
		pos2=in.find_first_of(sep, pos);
		if (pos2==stmiss) // no more terminators, EOL
			pos2=oob;
		out.push_back(in.substr(pos, pos2-pos));
		pos=pos2+1;
	}

	return (out.size()-nBefore);
}



void StrSubst(string &contents, const string &from, const string &to)
{
	tStrPos pos;
	while (stmiss!=(pos=contents.find(from)))
	{
		contents.replace(pos, from.length(), to);
		pos+=to.length();
	}
}


void Join(MYSTD::string &out, const MYSTD::string & sep, const tStrVec & tokens)
{
	out.clear();
	if(tokens.empty())
		return;
	
	for(tStrVec::const_iterator it=tokens.begin(); it!=tokens.end(); it++)
		out+=(sep + *it);
			
}


bool slow_ParseKeyValLine(const string & sIn, string & sOutKey, string & sOutVal)
{
	sOutVal.clear();
	sOutKey.clear();
	mstring *t = NULL;
	
	for(cmstring::const_iterator p=sIn.begin(); p!=sIn.end(); ++p)
	{
		if(t!=&sOutKey && strchr(SPACECHARS, UCHAR(*p))) // trim in key part and beginning of value
			continue;
		if(*p == ':')
		{
			if(sOutKey.empty())
				return false;
			t = &sOutVal;
			continue;
		}
		if(!t)
		{
			if(*p == '#') // comment line mark
				return false;

			t = &sOutKey;
		}
		t->push_back(*p);
	}
	
	bool ret = (t==&sOutVal && !sOutVal.empty());
	trimBack(sOutVal);
	return ret;
}



bool ParseKeyValLine(const string & sIn, string & sOutKey, string & sOutVal)
{
	// reuse the output string as buffer
	sOutVal=sIn;
	sOutKey.clear();
	trimFront(sOutVal);
	//cout << "parsing: "<<sOut<<endl;
	if(sOutVal.empty())
		return false;

	/*
	// comments or other crap, not for us
	if(stmiss!=sFilterString.find(sIn[0]))
		return false;
	*/

	string::size_type pos = sOutVal.find(":");
	if (pos==string::npos)
	{
		//cerr << "Bad configuration directive found, looking for: " << szKey << ", found: "<< sOut << endl;
		return false;
	}

	sOutKey = sOutVal.substr(0, pos);
	trimBack(sOutKey);

	sOutVal.erase(0, pos+1);
	trimFront(sOutVal);

	return true;
}


bool tHttpUrl::SetHttpUrl(string url)
{
	sPort.clear();
	sHost.clear();
	sPath.clear();
	
	trimBack(url);
	trimFront(url);
		
	if(url.empty())
		return false;
	
	tStrPos hStart(0), l=url.length(), hEndSuc(0), pStart(0), p;
	bool bCheckBrac=false;
	
	if(0==strncasecmp(url.c_str(), "http://", 7))
		hStart=7;
	/* checked below with others
	else if(url[0]=='/')
		hStart=1;
		*/
	else if(isalnum((UINT)url[0]))
		hStart=0;
	else if(url[0]=='[')
	{
		hStart=0;
		bCheckBrac=true; // must be closed
	}
	else if(stmiss!=url.find("://"))
		return false; // other protocol or weird stuff
	
	// kill leading slashes in any case
	while(hStart<l && url[hStart]=='/') hStart++;
	if(hStart>=l)
		return false;
	
	hEndSuc=url.find('/', hStart);
	if(stmiss==hEndSuc)
	{
		hEndSuc=l;
		goto extract_host_check_port;
	}
	pStart=hEndSuc;
	while(pStart<l && url[pStart]=='/') pStart++;
	pStart--;
	
	extract_host_check_port:
	if(pStart==0)
		sPath="/";
	else
		sPath=url.substr(pStart);

	if(url[hStart]=='_') // those are reserved
		return false;
	
	sHost=url.substr(hStart, hEndSuc-hStart);

	l=sHost.size();
	p=sHost.rfind(':');
	if(p==stmiss)
		goto strip_ipv6_junk;
	else if(p==l-1)
		return false; // this is crap, http:/asdf?
	else for(tStrPos r=p+1;r<l;r++)
		if(! isdigit(sHost[r]))
			return false;
	
	sPort=sHost.substr(p+1);
	sHost.erase(p);
	
	strip_ipv6_junk:
	
	if(sHost[0]=='[')
	{
		bCheckBrac=true;
		sHost.erase(0,1);
	}
	
	if(sHost[sHost.length()-1]==']')
		sHost.erase(sHost.length()-1);
	else if(bCheckBrac) // must have been present here
		return false;
	
	return true;
	
}

string tHttpUrl::ToURI() const 
{ 
	return string("http://") + (sPort.empty()? (sHost+sPath) : (sHost+":"+sPort+sPath));
}
//tHttpUrl::tHttpUrl() : pMisc(NULL) {};

/*
const MYSTD::string GetDateString() {
    char buf[26];
    struct tm tmp;
    const time_t cur=time(NULL);
    gmtime_r(&cur, &tmp);
    asctime_r(&tmp, buf);
    buf[24]=0x0;
//    cout << "generated date: " << buf<<endl;
    return buf;
    
}
*/

#if defined(HAVE_WORDEXP) || defined(HAVE_GLOB)

tStrDeq ExpandFilePattern(const string &pattern, bool bSorted)
{
	tStrDeq srcs;
#ifdef HAVE_WORDEXP
	wordexp_t p;
	memset(&p, 0, sizeof(p));
    if(0==wordexp(pattern.c_str(), &p, 0))
    {
    	for(char **s=p.we_wordv; s<p.we_wordv+p.we_wordc;s++)
    		srcs.push_back(*s);
    	wordfree(&p);
    }
    if(bSorted) MYSTD::sort(srcs.begin(), srcs.end());
#elif defined(HAVE_GLOB)
	glob_t p;
	memset(&p, 0, sizeof(p));
	if(0==glob(pattern.c_str(), GLOB_DOOFFS | (bSorted ? 0 : GLOB_NOSORT),
			NULL, &p))
	{
		for(char **s=p.gl_pathv; s<p.gl_pathv+p.gl_pathc;s++)
			srcs.push_back(*s);
		globfree(&p);
	}
#else
#warning Needs a file name expansion function, wordexp or glob
	srcs.push_back(pattern);
#endif

	return srcs;
}
#endif

#ifndef MINIBUILD

bool IsAbsolute(cmstring &dirToFix)
{
	bool bAbs=false;
#ifdef WIN32
	bAbs=dirToFix.length()> 2 && CPATHSEPWIN==dirToFix[2] && ':'==dirToFix[1] && isalpha(dirToFix[0];
	if(!bAbs) // maybe unc path?
		bAbs=(dirToFix[0] == CPATHSEPWIN &&dirToFix[1] == CPATHSEPWIN);
#else
	bAbs = (!dirToFix.empty() && CPATHSEPUNX==dirToFix[0]);
#endif
	return bAbs;
}

/*
void MakeAbsolutePath(MYSTD::string &dirToFix, const MYSTD::string &reldir)
{
	if(!IsAbsolute(dirToFix))
		dirToFix=reldir+CPATHSEP+dirToFix;
}
*/

void HttpEscape(string &s)
{
	tStrPos p;
	while(stmiss!=(p=s.find_first_of("~ ")))
	{
		switch(s[p])
		{
		case('~'): s.replace(p, 1, "%7e"); break;
		case(' '): s.replace(p, 1, "&nbsp"); break;
		}
	}
}

extern uint_fast16_t hexmap[];

bool UrlDecode(const MYSTD::string &from, MYSTD::string & to)
{
	bool ret=true;
	for(string::size_type i=0; i<from.length(); i++)
	{
		if(from[i] != '%')
			to+=from[i];
		else if(i>from.length()-3 // cannot start another sequence here (too short), keep as-is
				|| hexmap[(UCHAR)from[i+1]]>15 // not hex?
				|| hexmap[(UCHAR)from[i+2]]>15)
		{
			ret=false;
		}
		else
		{
			to+=char(16*hexmap[(unsigned char) from[i+1]] + hexmap[(unsigned char) from[i+2]]);
			i+=2;
		}
	}
	return ret;
}

mstring UrlDecode(cmstring &from)
{
	mstring ret; // let the compiler optimize
	UrlDecode(from, ret);
	return ret;
}

cmstring sEmptyString("");

/*
int GetSimilarity(cmstring& wanted, cmstring& candidate)
{
	const char *w=wanted.c_str(), *c=candidate.c_str();
	while(w&&c)
	{

	}
}
*/

#endif
