#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#ifdef WIN32
#include <io.h>
#else
#include <sys/types.h>
#include <dirent.h>
#endif
#include "dir.h"
#include "lib.h"
#include "emsg.h"
#define DMID 14
#include "debug.h"
#include "lprintf.h"
#define MODE_DIR 0700
#include "vsnprintf.h"
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
extern int dmsg(const char *arg_list, ...);
void wild_free(char *names[], int n)
{
	int i;
	for (i=0; i<n; i++) {
		if (names[i]!=NULL) free(names[i]); names[i] = NULL;
	}
}
#ifdef WIN32
int wild_find(char *wild, char *names[], int max, int *size)
{
    struct _finddata_t info;
	intptr_t handle,status;
	int n=0;
	char bf[300],dadd[300];
	char *s;
	char *last=NULL;

	*size = 0;
	strcpy(bf,wild);
	for (s=bf;*s!=0;s++) {
		if (*s=='/') last = s;
		if (*s=='\\') last = s;
	}
	if (last==NULL) strcpy(dadd,"");
	else {last[1]=0; strcpy(dadd,bf);}


	handle = _findfirst(wild,&info);
    status = handle;
    while (status!=-1) {
		sprintf(bf,"%s%s",dadd,info.name);

		if (n>=max) break;
		names[n++] = strdup(bf);
		*size += info.size;
        status = _findnext(handle,&info);
    }
    if (handle!=-1) _findclose(handle);
	return n;
}

int wild_find_dir(char *wild, char *names[], int max)
{
    struct _finddata_t info;
	intptr_t handle,status;
	int n=0;
	char bf[300],/*dname[300],*/dadd[300];
	char *s;
	char *last=NULL;
	/* Matt Changed prev section to:*/
	strcpy(bf,wild);
	for (s=bf;*s!=0;s++) {
		if (*s=='/') last = s;
		if (*s=='\\') last = s;
	}
	if (last==NULL) strcpy(dadd,"");
	else {last[1]=0; strcpy(dadd,bf);}
/*	dmsg("Scan wild dir=(%s) add=(%s) \n",wild,dadd);       */

    handle = _findfirst(wild,&info);
    status = handle;
    while (status!=-1) {
		if ((info.attrib & _A_SUBDIR) !=0) {
			if (strcmp(info.name,".")!=0 && strcmp(info.name,"..")!=0) {

				sprintf(bf,"%s%s",dadd,info.name);
				if (n>=max) break;
				names[n++] = strdup(bf);
			}

		}
		status = _findnext(handle,&info);

    }
    if (handle!=-1) _findclose(handle);
	return n;
}

#else
int wild_find_2(char *wild, char *names[], int max, int *tsize, int dir_only);

int wild_find(char *wild, char *names[], int max, int *tsize)
{
	return wild_find_2(wild,names,max,tsize,FALSE);
}
int wild_find_dir(char *wild, char *names[], int max)
{
	int tsize;
	return wild_find_2(wild,names,max,&tsize,TRUE);
}

int wild_find_2(char *wild, char *names[], int max, int *tsize, int dir_only)
{
    DIR *dirp;
	int n=0;
	char ch;
    struct dirent *direntp;
	char dresult[3000 + sizeof(struct dirent)];
	char bf[BFSZ],bf2[BFSZ],dname[BFSZ],dadd[BFSZ];
	char *s;
	char *last=NULL;

/* Start of Matt's change:*/
	ncpy(bf,wild,BFSZ-1);
	for (s=bf;*s!=0;s++) {
		if (*s=='/') last = s;
		if (*s=='\\') last = s;
	}
	if (last==NULL) {
		strcpy(dadd,"");
		strcpy(dname,".");

	}
	else {
		ch=last[1];
		last[1]=0;
		ncpy(dadd,bf,BFSZ-1);
		last[0]=0;
		ncpy(dname,bf,BFSZ-1);
		last[1]=ch;
		wild=last+1;
	}
	imsg("Scan wild dname=(%s) wild=(%s) add=(%s) dironly=%d\n",dname,wild,dadd,dir_only);
/*End of Matt's change*/

    dirp = opendir(dname);
	*tsize=0;
    if (dirp==NULL) {perror("Could not read directory file"); return 0;}

    while ((safe_readdir(dirp,dresult,&direntp))==0) {
		if (direntp==NULL) break;
        if (match_wild(wild,direntp->d_name)) {
            if (strcmp(direntp->d_name,".")==0) continue;
            if (strcmp(direntp->d_name,"..")==0) continue;
            lprintf(bf2,BFSZ-1,"%s%s",dadd,direntp->d_name);
			if (dir_only && !lib_is_dir(bf2)) continue;
			if (lib_is_file(bf2)) {
				*tsize+=lib_file_size(bf2);
			}
            names[n++] = strdup(bf2);
		}
		if (n>=max) break;
	}
    closedir(dirp);
	return n;
}
#endif

#include "defines.h"
#define pmsg printf
int do_xlist_real(char *src, char *dst, void *dptr, int(*fn)(char*,char*,void *)) {
	WILD_DATA data;
	WILD_HANDLE *h = NULL;
	char file[BFSZ];
	char path[BFSZ];
	char wild[BFSZ];
	char *p;
	int r;


	if (!wild_first(&h,src,&data)) {
		wild_end(&h);
		set_last_copy_error("Failed to copy (%s) no files found: %s",src,strerror(errno));
		return 0;
	}

	ncpy(path,src,BFSZ-1);
	for(p = &path[lib_strlen(path)]; p != path; p--) {
		if (issep(*p)) {p++; break;}
	}
	if (p == path && issep(*p)) p++;
	ncpy(wild,p,BFSZ-1);   //  wild = strdup(p); // memory leak? 
	*p = 0;

	r = 0;
	do {
		if (data.name[0] == '.' && (!data.name[1] || (data.name[1] == '.' && !data.name[2])))
			continue;

		lprintf(file,BFSZ,"%s%s",path,data.name);

		if (dst && data.isdir) {
			char newsrc[BFSZ];
			char newdst[BFSZ];

			make_path(file,wild,newsrc,BFSZ);
			make_path(dst,data.name,newdst,BFSZ);
			make_dir(newdst);
			r += do_xlist_real(newsrc,newdst,dptr,fn);
			continue;
		}
		
		if (dptr) r += fn(file,dst,dptr);
		else r += ((int(*)(char*,char*))fn)(file,dst);

	} while(wild_next(h,&data));
	
	wild_end(&h);
	return r;
}
int do_xlist(char *src, char *dst, int(*fn)(char*,char*)) {
	return do_xlist_real(src,dst,NULL,(int(*)(char*,char*,void*))fn);
}
int do_xlist_ex(char *src, char *dst, void *dptr, int(*fn)(char*,char*,void*)) {
	return do_xlist_real(src,dst,dptr,fn);
}
/* RMH this has hard limit of 9000 files (actually 8998 as . and .. are found first)
int do_xlist_old(char *src, char *dst, int(*fn)(char*,char*)) {
	int nwild,tsize,i,r,length;
	char *wild_names[10000],*p;

	nwild = wild_find(src,wild_names,9000,&tsize);
	if (nwild == 0) return 0;

	length = lib_strlen(src);
	for( p = src + lib_strlen(src) - 1; p != src; p--) {
		if (issep(*p)) break;
		if (*p == '.') {
			length = p-src;
			break;
		}
	}
	for (i=r=0; i<nwild; i++) {
		if (strcmp(&wild_names[i][length-1],".") == 0 || strcmp(&wild_names[i][length-1],"..") == 0) continue;
		if (fn(wild_names[i],dst))
			r++;
	}
	wild_free(wild_names,nwild);
	return r;
}
*/
int do_xcopy(char *src, char *dst, int required)
{
	char ** wild_names = calloc(sizeof(char *), 10000); //  [10000] ;
	int i,r;
	int nwild;
	int tsize;

	nwild = wild_find(src,wild_names,9000,&tsize);
	if (nwild==0 && required) {
		set_last_copy_error("Installation files missing {%.500s}",src);
		free(wild_names);
		return FALSE;
	}
	r = 0;
	for (i=0; i<nwild; i++) {
		if (do_copy(wild_names[i],dst,TRUE))
			r++;
	}
	wild_free(wild_names,nwild);
	free(wild_names);
	return r;
}
int do_wild_delete(char *src)
{
	char **wild_names = calloc(sizeof(char *),10000);
	int i;
	int nwild;
	int tsize;
	int ndel=0;
	//imsg("do_wild_delete(%s)\n",src);
	lib_del_isok(src);
	nwild = wild_find(src,wild_names,9000,&tsize);
	for (i=0; i<nwild; i++) {
		if (strstr(wild_names[i],"..")!=NULL) continue;
//		printf("Delete (%s)\n",wild_names[i]);
		ndel++;
		lib_remove(wild_names[i]);
	}
	wild_free(wild_names,nwild);
	free(wild_names);
	//if (ndel>0) imsg("do_wild_delete removed %d files from %s\n",ndel,src);
	return nwild;
}
void do_xcopy_rename(char *src, char *xdst, char *oldext, char *newext)
{
	char **wild_names = calloc(sizeof(char *),10000);
	int i;
	int nwild;
	int tsize;
	char *last;
	char *s;
	char dst[BFSZ];
	int required = TRUE;

	nwild = wild_find(src,wild_names,9000,&tsize);
	if (required) if (nwild==0) pmsg("Installation files missing {%.500s} \n",src);
	for (i=0; i<nwild; i++) {
		src = wild_names[i];
		last = src;
		for (s=src;*s!=0;s++) {
			if (*s=='/') last=s+1;
		}
		sprintf(dst,"%.500s/%.500s",xdst,last);
		s = strstr(dst,oldext);
		if (s!=NULL) strncpy(s,newext,100);
//		printf("Copy (%.500s) to (%.500s)\n",wild_names[i],dst);
		do_copy_raw(wild_names[i],dst,TRUE);
	}
	wild_free(wild_names,nwild);
	free(wild_names);

}
int do_copy(char *src, char *xdst, int over_write) {
	char dst[BFSZ];
	char *last,*s;

	last = src;
	for (s=src;*s!=0;s++) {
		if (issep(*s)) last=s+1;
	}
	sprintf(dst,"%.500s/%.500s",xdst,last);
	return do_copy_raw(src,dst,over_write);
}
int do_copy_exe_busy(char *src, char *xdst, int over_write) {
	char dst[BFSZ];
	char dst2[BFSZ];
	char old[BFSZ];
	char *last,*s;
	int r;

	last = src;
	for (s=src;*s!=0;s++) {
		if (issep(*s)) last=s+1;
	}
	sprintf(dst,"%.500s/%.500s",xdst,last);
	sprintf(dst2,"%s_oldexe",dst);
	if (over_write) if (lib_exists(dst)) {
		remove(dst2);
		rename(dst,dst2); // chrisp addition so that osx is happy as it doesn't like binaries to be coppied over binaries. 
		remove(dst2);  // and get rid of the old file.  
	}
	r = do_copy_raw(src,dst,over_write);
	if (!r && errno != ENOENT) { //failed and 'src' exists
		lprintf(old,BFSZ,"%s.old",dst);
		if (remove(old) == -1 && errno != ENOENT) //failed and 'old' exists
			set_last_copy_error("do_copy_exe_busy: failed to remove(%s): %s",old,strerror(errno));
		if (rename(dst,old) == -1 && errno != ENOENT) //failed and 'dst' exists
			set_last_copy_error("do_copy_exe_busy: failed to rename(%s,%s): %s",dst,old,strerror(errno));
		r = do_copy_raw(src,dst,over_write);
	}
#ifndef WIN32
	if (r) chmod(dst,0755);
#endif
	return r;
}
int do_copy_exe(char *src, char *xdst, int over_write) {
	char dst[BFSZ];
	char *last,*s;
	int r;

	last = src;
	for (s=src;*s!=0;s++) {
		if (issep(*s)) last=s+1;
	}
	sprintf(dst,"%.500s/%.500s",xdst,last);

	r = do_copy_raw(src,dst,over_write);
#ifndef WIN32
	chmod(dst,0755);
#endif
	return r;
}


static int do_copy_uid = -1, do_copy_gid = -1;
void do_copy_setown(int uid, int gid) {
	do_copy_uid = uid;
	do_copy_gid = gid;
}

static char do_copy_raw_last_error[BFSZ];
void set_last_copy_error(char *format, ...) {
	va_list arg;

	va_start(arg,format);
	vsnprintf(do_copy_raw_last_error,BFSZ-1,format,arg);
}
char *last_copy_error(void) {
	return do_copy_raw_last_error;
}

int do_copy_raw(char *src, char *dst, int over_write) {
	FILE *fsrc,*fdst;
	int r;
	char bf[BFSZ];

	if (!over_write && lib_exists(dst)) {
//		imsg("Not copying [%.500s] to [%.500s] since [%.500s] already exists\n",src,dst,dst);
		return -TRUE; // was -true,  dodgy usage.  
	}
//	imsg("Copying [%.500s] to [%.500s]\n",src,dst);

	fsrc = fopen(src,"rb");
	if (fsrc==NULL) {
		set_last_copy_error("Failed to copy, unable to read '%s', %s",src,strerror(errno));
		return FALSE;
	}
	fdst = fopen(dst,"wb");
	if (fdst==NULL) {
#ifndef WIN32
		if (errno==ETXTBSY) sleep(8);
#endif
		fdst = fopen(dst,"wb");
	}
	if (fdst==NULL) {
		set_last_copy_error("Failed to copy, unable to write '%s', %s",dst,strerror(errno));
		fclose(fsrc);
		return FALSE;
	}
	for (;!feof(fsrc);) {
		r = (int)fread(bf,1,1000,fsrc);
		if (r<=0) break;
		if ((int) fwrite(bf,1,r,fdst)!=r) perror(dst);
	}
	fclose(fsrc);
	if(fclose(fdst)!=0) {
		set_last_copy_error("Could not close destination file '%s', %s",dst,strerror(errno));
		return FALSE;
	}
	lib_chown(dst,do_copy_uid,do_copy_gid);
	return TRUE;
}
int delfn(char *file, char *null) {
	char bf[BFSZ];
	int r = 0;

	if (lib_isdir(file)) {
		dmsg("Delete all files in [%s]\n",file);
		lprintf(bf,BFSZ-1,"%s/*",file);
		r += do_xlist(bf,NULL,delfn);
		dmsg("Remove dir [%s]\n",file);
		r += (rmdir(file) == 0)?1:0;
		return r;
	}
	dmsg("Delete [%s]\n",file);
	return (lib_remove(file) == 0)?1:0;
}
int delete_dir(char *dir) {
	char bf[BFSZ];
	int r = 0;

	lib_del_isok(dir);
	dmsg("Delete all files in [%s]\n",dir);
	lprintf(bf,BFSZ-1,"%s/*",dir);
	r += do_xlist(bf,NULL,delfn);
	r += (rmdir(dir) == 0)?1:0;
	return r;
}
int make_dir(char *dir) {
	int r = lib_mkdir(dir,MODE_DIR);
	lib_chown(dir,do_copy_uid,do_copy_gid);
	return r;
}
