#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include "lib.h"
#include "treecopy.h"
#include "dir.h"
#define DMID 22
#include "debug.h"
#define issep(x) (x=='/'||x=='\\')
#include "defines.h"
#define FALSE 0
#define TRUE !FALSE

static char *attach_path(char *to, char *path, int bfsz) {
	if ( to && path && bfsz > 0 ) {
		int index;

		for ( index = 0; index < bfsz && to[index]; index++ ) {
			if ( issep(to[index]) ) to[index] = '/';
		}
		if ( issep(to[index-1]) ) {
			if ( issep(path[0]) ) path++;
		}
		else {
			if ( !issep(path[0]) ) to[index++] = '/';
		}
		for ( ; index < bfsz && *path; index++, path++ ) {
			if ( issep(*path) ) to[index] = '/';
			else to[index] = *path;
		}
		to[index] = '\0';
	}
	return to;
}

static char *strip_filename(char *path) {
	char *ptr;

	for ( ptr = path+lib_strlen(path)-1; ptr != path; ptr-- ) {
		if ( issep(*ptr) ) {
			*ptr = '\0';
			break;
		}
	}
	return path;
}

#define MODE_DIR 0700

/* chown does not modify id if it is set to -1 */
static int treecopy_uid = -1,treecopy_gid = -1;
static void (*treecopy_cb)(char *, void *);
static void *treecopy_pcb;

void treecopy_callbk(void (*fn)(char *, void *), void *pcb) {
	treecopy_pcb = pcb;
	treecopy_cb = fn;
}
void treecopy_setown(int uid, int gid) {
	treecopy_uid = uid;
	treecopy_gid = gid;
}

int tree_chown(char *dir) {
	//imsg("tree_chown dir(%s)\n",dir);
	return tree_chown_real(dir,"*");
}

int tree_chown_real(char *dir, char *wild) {
#ifdef WIN32
#else
	WILD_HANDLE *h;
	WILD_DATA data;
	char bf[BFSZ];

	//imsg("tree_chown_real dir(%s) wild(%s) starts\n",dir,wild);
	lib_chown(dir,treecopy_uid,treecopy_gid);
	ncpy(bf,dir,BFSZ-1);
	attach_path(bf,wild,BFSZ-1);
	//imsg("tree_chown_real wild_first bf(%s)\n",bf);
	if (wild_first(&h,bf,&data)) {
		do {
			if (data.islink) continue;
			if (!strcmp(data.name, ".") || !strcmp(data.name,"..")) continue;
			ncpy(bf,dir,BFSZ-1);
			attach_path(bf,data.name,BFSZ-1);
			if (data.isdir) tree_chown(bf);
			else lib_chown(bf,treecopy_uid,treecopy_gid);
		}
		while (wild_next(h,&data));
	}
	wild_end(&h);
	//imsg("tree_chown_real dir(%s) wild(%s) done\n",dir,wild);
#endif
	return TRUE;
}
int wrapper_treecb(char *path, void *param)
{
	if (treecopy_cb) {
		treecopy_cb(path,param);
	}
	return true;
}


int treecopy(char *wild, char *dst, int overwrite, char *reason) {
	return treecopy_wcb(wild,dst,overwrite,reason,wrapper_treecb,treecopy_pcb);
}
int treecopy_wcb(char *wild, char *dst, int overwrite, char *reason,int  (*treecopy_cb)(char *, void *), void *treecopy_pcb) {
	char dest[BFSZ],src[BFSZ],tmp[BFSZ],*p;
	WILD_HANDLE *h;
	WILD_DATA data;
	int r,x;


    /*imsg("TREECOPY wild=[%s], dst=[%s]\n",wild,dst);*/

	ncpy(dest, dst, BFSZ-1);
	r = lib_strlen(dest);
	if (r != BFSZ-1 && !issep(dest[r-1])) {
		dest[r++] = '/';
		dest[r] = '\0';
	}

	if (!lib_create_dir(dest, MODE_DIR)) {
		set_last_copy_error("Failed to create '%s', %s",dest,strerror(errno));
		sprintf(reason,"Failed to create (%s) %s",dest,strerror(errno));
		return 0;
	}
	lib_chown(dest,treecopy_uid,treecopy_gid);
	do_copy_setown(treecopy_uid,treecopy_gid);
	r = 0;

	if ( wild_first(&h, wild, &data) ) {
		/*imsg("TREECOPY Found [%s]\n",data.name);*/
		do {
			if (data.islink) continue;
			/*imsg("TREECOPY Found2 [%s]\n",data.name);*/
			if ( !strcmp(data.name, ".") || !strcmp(data.name,"..") ) continue;
			ncpy(src, wild, BFSZ-1);
			attach_path(strip_filename(src), data.name, BFSZ-1);
			if ( data.isdir ) {
				attach_path(src, "*", BFSZ-1);

				ncpy(dest, dst, BFSZ-1);
				attach_path(dest, data.name, BFSZ-1);

				x = treecopy(src, dest, overwrite,reason);
				if (x) r += x;
			}
			else {
                /*imsg("TREECOPY src=[%s], dst=[%s]\n",src,dst);*/
				int copy_that = 1;
				if (treecopy_cb) {
					for(p = src + lib_strlen(src); p != src && !issep(*p); p--);
					ncpy(tmp,dst,BFSZ-1);
					ncat(tmp,p,BFSZ-1);
					if (!treecopy_cb(tmp,treecopy_pcb)) copy_that = 0;
				}
				if (copy_that && do_copy(src, dst, overwrite))					
					r++;
			}
		}
		while ( wild_next(h, &data) );
	}
    /*else emsg("TREECOPY wild_first failed %s\n",strerror(errno));*/
	wild_end(&h);
	if (r == 0) set_last_copy_error("No files found");
	if (r==0) sprintf(reason,"No files found (%s) %s",wild,strerror(errno));
	return r;
}
