#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <fs++/general.h>

#include <sys/stat.h>
#include <unistd.h>
#include <sstream>

#include <map>
#include <string>

static std::map<std::string, struct stat> dir_map;

namespace filesystem
	{

		string
		FExpand(const string& path) {
			string tmp = path; 
			int i,j = 0;

			/* Replace ~/ with $HOME */
			if ((path[0]=='~') && ((path.length()==1) || (path[1]=='/')))
				tmp.replace(1,1, getenv("HOME"));
			if ((path.length()==0) || (path[0]!='/'))
				tmp = getWorkingDirectory() + path;

			/* First remove all references to '/./' & // */

			while ((i=tmp.rfind("/./"))>0) tmp.replace(i,2,"");
				while ((i=tmp.rfind("//"))>0) tmp.replace(i,1,"");

			/* Now remove also all references to '/../' + of course previous dirs.. */
			while ((i=tmp.find("/../"))>0)
			  if (i > 1) {    /* Find the pos of the previous dir */
			    j=tmp.rfind('/',i-1);
			    tmp.replace(j,i-j+3,"");
			  } else        /* i==1, so we have tmp='/../something', just delete '/..' */
			    tmp.replace(1,3,"");

			/* Remove ending /.. */
			if ((i=tmp.rfind("/.."))==((int)tmp.length()-2))
			  if (i > 1) {     /* Find the pos of the previous dir */
			    j=tmp.rfind('/',i-1);
			    tmp.replace(j,i-j+2,"");
			  } else        /* i==1, so we have tmp='/..', just delete it */
			    tmp = "";

			/* if last character is / then remove it - dir is also a file :-) */
			if ((tmp.length()) && (tmp[tmp.length()]=='/'))
			  tmp.replace(tmp.length(),tmp.length(),"");

			return tmp;
		}

		bool
		isDirectory (const string& filename)
			{
			  std::map<std::string, struct stat>::iterator i;
			  
			  if ((i = dir_map.find(filename)) != dir_map.end())
			    return S_ISDIR(i->second.st_mode);
			  else {

				struct stat statbuf ;
				string wd = filename;

#ifndef RECURSE_LINKS
				for (int l=0;l<1024;l++) // stay coherent with kernel
					{
#endif
						if (lstat (wd.c_str (), &statbuf) != 0)
							return false ;	// could not stat file

						if (S_ISLNK(statbuf.st_mode))
							{
								const int maxbufsize = 2048;
								char buf[maxbufsize];
								if (int count = readlink(wd.c_str(),buf,maxbufsize))
									{
										if (buf[0]=='/')
											wd.erase();
										else
											wd.replace(wd.rfind('/')+1,wd.length(),"");
										wd.append(buf,count);
										wd = FExpand(wd);
#ifdef RECURSE_LINKS
										return isDirectory(wd);
									} else
										return false;
							} else
#else
									} else
										return false;
							} else
								break;
					} // for
#endif
	  dir_map.insert(std::make_pair(filename, statbuf));
		return S_ISDIR(statbuf.st_mode);
}

			}

		const string
		getWorkingDirectory ()
			{
				string wd ;

#				ifdef _GNU_SOURCE

					char * wd_cstr = get_current_dir_name () ;

					if (wd_cstr)
						{
							wd = wd_cstr ;
							free(wd_cstr) ;
						}
					else
						wd = "." ;

#				else

					const int maxbufsize = 2048 ;
					char buf[maxbufsize] ;

					if (getcwd (buf, maxbufsize) != 0)
						{
							wd = buf ;
						}
					else
						wd = "." ;

#				endif

				return wd ;
			}

		int
		getFileSize (const string& filename)
			{
				struct stat statbuf ;

				if (stat (filename.c_str (), &statbuf) != 0)
					return -1 ;	// could not stat file

				return statbuf.st_size ;
			}

		void
		getLine (std::istream& in, string& buf)
			{
			std::ostringstream tmp;
			tmp << in;
			buf = tmp.str();
			}

	}	// namespace filesystem
